diff --git a/Cargo.lock b/Cargo.lock index 6b61bbe3..988a322c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,300 +2,172 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "actix" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "671ce3d27313f236827a5dd153a1073ad03ef31fc77f562020263e7830cf1ef7" -dependencies = [ - "actix-http", - "actix-rt", - "actix_derive", - "bitflags", - "bytes", - "crossbeam-channel 0.3.9", - "derive_more 0.14.1", - "futures", - "hashbrown 0.3.1", - "lazy_static", - "log", - "parking_lot 0.8.0", - "smallvec 0.6.14", - "tokio-codec", - "tokio-executor", - "tokio-io", - "tokio-tcp", - "tokio-timer", - "trust-dns-resolver", -] - [[package]] name = "actix-codec" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f2c11af4b06dc935d8e1b1491dad56bfb32febc49096a91e773f8535c176453" -dependencies = [ - "bytes", - "futures", - "log", - "tokio-codec", - "tokio-io", -] - -[[package]] -name = "actix-connect" -version = "0.2.5" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fade9bd4bb46bacde89f1e726c7a3dd230536092712f5d94d77ca57c087fca0" +checksum = "57a7559404a7f3573127aab53c08ce37a6c6a315c374a31070f3c91cd1b4a7fe" dependencies = [ - "actix-codec", - "actix-rt", - "actix-service", - "actix-utils", - "derive_more 0.15.0", - "either", - "futures", - "http", + "bitflags 1.3.2", + "bytes 1.4.0", + "futures-core", + "futures-sink", "log", - "tokio-current-thread", - "tokio-tcp", - "trust-dns-resolver", + "memchr", + "pin-project-lite", + "tokio", + "tokio-util", ] [[package]] name = "actix-cors" -version = "0.1.0" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66e5b071c68ac8ab182e7b7717167ef29ea93e166bc26391f678f19ac08ed129" +checksum = "b340e9cfa5b08690aae90fb61beb44e9b06f44fe3d0f93781aaa58cfba86245e" dependencies = [ - "actix-service", + "actix-utils", "actix-web", - "derive_more 0.14.1", - "futures", + "derive_more", + "futures-util", + "log", + "once_cell", + "smallvec", ] [[package]] name = "actix-http" -version = "0.2.11" +version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb50f77cd28240d344fd54afd205bae8760a3b0ad448b1716a2aa31e24db139" +checksum = "c2079246596c18b4a33e274ae10c0e50613f4d32a4198e09c7b93771013fed74" dependencies = [ "actix-codec", - "actix-connect", - "actix-server-config", + "actix-rt", "actix-service", - "actix-threadpool", "actix-utils", + "ahash 0.8.3", "base64", - "bitflags", - "brotli2", - "bytes", - "chrono", - "copyless", - "derive_more 0.15.0", - "either", + "bitflags 1.3.2", + "bytes 1.4.0", + "bytestring", + "derive_more", "encoding_rs", - "failure", - "flate2", - "futures", + "futures-core", "h2", - "hashbrown 0.6.3", - "http", + "http 0.2.9", "httparse", - "indexmap", + "httpdate", + "itoa 1.0.6", "language-tags", - "lazy_static", - "log", + "local-channel", "mime", - "percent-encoding 2.1.0", - "rand 0.7.3", - "regex", - "serde", - "serde_json", - "serde_urlencoded 0.6.1", + "percent-encoding 2.2.0", + "pin-project-lite", + "rand 0.8.5", "sha1", - "slab", - "time", - "tokio-current-thread", - "tokio-tcp", - "tokio-timer", - "trust-dns-resolver", + "smallvec", + "tokio", + "tokio-util", + "tracing", ] [[package]] name = "actix-router" -version = "0.1.5" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23224bb527e204261d0291102cb9b52713084def67d94f7874923baefe04ccf7" +checksum = "d66ff4d247d2b160861fa2866457e85706833527840e4133f8f49aa423a38799" dependencies = [ - "bytes", - "http", - "log", + "bytestring", + "http 0.2.9", "regex", "serde", - "string", + "tracing", ] [[package]] name = "actix-rt" -version = "0.2.6" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88c9da1d06603d82ec2b6690fc5b80eb626cd2d6b573f3d9a71d5252e06d098e" +checksum = "15265b6b8e2347670eb363c47fc8c75208b4a4994b27192f345fcbe707804f3e" dependencies = [ - "actix-threadpool", - "copyless", - "futures", - "tokio-current-thread", - "tokio-executor", - "tokio-reactor", - "tokio-timer", + "futures-core", + "tokio", ] [[package]] name = "actix-server" -version = "0.6.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd626534af8d0a738e5f74901fe603af0445708f91b86a7d763d80df10d562a5" +checksum = "3e8613a75dd50cc45f473cee3c34d59ed677c0f7b44480ce3b8247d7dc519327" dependencies = [ "actix-rt", - "actix-server-config", "actix-service", - "futures", - "log", + "actix-utils", + "futures-core", + "futures-util", "mio", - "net2", "num_cpus", - "slab", - "tokio-io", - "tokio-reactor", - "tokio-signal", - "tokio-tcp", - "tokio-timer", -] - -[[package]] -name = "actix-server-config" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "483a34989c682d93142bacad6300375bb6ad8002d2e0bb249dbad86128b9ff30" -dependencies = [ - "futures", - "tokio-io", - "tokio-tcp", + "socket2", + "tokio", + "tracing", ] [[package]] name = "actix-service" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca5b48e928841ff7e7dce1fdb5b0d4582f6b1b976e08f4bac3f640643e0773f" -dependencies = [ - "futures", -] - -[[package]] -name = "actix-testing" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af001e97ac6750994824d400a1b7087055aab14317aa012f528d0b2b363f37f1" -dependencies = [ - "actix-rt", - "actix-server", - "actix-server-config", - "actix-service", - "futures", - "log", - "net2", - "tokio-reactor", - "tokio-tcp", -] - -[[package]] -name = "actix-threadpool" -version = "0.1.2" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b5ae85d13da7e6fb86b1b7bc83185e0e3bd4cc5f421c887e1803796c034d35d" +checksum = "3b894941f818cfdc7ccc4b9e60fa7e53b5042a2e8567270f9147d5591893373a" dependencies = [ - "derive_more 0.15.0", - "futures", - "lazy_static", - "log", - "num_cpus", - "parking_lot 0.9.0", - "threadpool", + "futures-core", + "paste", + "pin-project-lite", ] [[package]] name = "actix-utils" -version = "0.4.7" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "908c3109948f5c37a8b57fd343a37dcad5bb1d90bfd06300ac96b17bbe017b95" +checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8" dependencies = [ - "actix-codec", - "actix-service", - "bytes", - "either", - "futures", - "log", - "tokio-current-thread", - "tokio-timer", + "local-waker", + "pin-project-lite", ] [[package]] name = "actix-web" -version = "1.0.9" +version = "4.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af3a1b967cdbacb903c4b9ae71257a7f098d881b25eb483d0c468b7dac579b03" +checksum = "cd3cb42f9566ab176e1ef0b8b3a896529062b4efc6be0123046095914c4c1c96" dependencies = [ "actix-codec", "actix-http", "actix-router", "actix-rt", "actix-server", - "actix-server-config", "actix-service", - "actix-testing", - "actix-threadpool", "actix-utils", - "actix-web-codegen", - "awc", - "bytes", - "derive_more 0.15.0", + "ahash 0.7.6", + "bytes 1.4.0", + "bytestring", + "cfg-if", + "derive_more", "encoding_rs", - "futures", - "hashbrown 0.6.3", + "futures-core", + "futures-util", + "http 0.2.9", + "itoa 1.0.6", + "language-tags", "log", "mime", - "net2", - "parking_lot 0.9.0", + "once_cell", + "pin-project-lite", "regex", "serde", "serde_json", - "serde_urlencoded 0.6.1", - "time", - "url 2.2.2", -] - -[[package]] -name = "actix-web-codegen" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "068a33520e21c1eea89726be4d6b3ce2e6b81046904367e1677287695a043abb" -dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.20", - "syn 1.0.98", -] - -[[package]] -name = "actix_derive" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf5f6d7bf2d220ae8b4a7ae02a572bb35b7c4806b24049af905ab8110de156c" -dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "syn 0.15.44", + "serde_urlencoded", + "smallvec", + "socket2", + "time 0.3.20", + "url 2.3.1", ] [[package]] @@ -304,40 +176,7 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e61f2b7f93d2c7d2b08263acaa4a363b3e276806c68af6134c44f523bf1aacd" dependencies = [ - "gimli 0.25.0", -] - -[[package]] -name = "addr2line" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" -dependencies = [ - "gimli 0.26.2", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "ahash" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29661b60bec623f0586702976ff4d0c9942dcb6723161c2df0eea78455cfedfb" -dependencies = [ - "const-random", -] - -[[package]] -name = "ahash" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217" -dependencies = [ - "const-random", + "gimli", ] [[package]] @@ -346,49 +185,61 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ - "getrandom 0.2.7", + "getrandom 0.2.8", "once_cell", "version_check", ] [[package]] name = "ahash" -version = "0.8.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57e6e951cfbb2db8de1828d49073a113a29fd7117b1596caa781a258c7e38d72" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" dependencies = [ - "cfg-if 1.0.0", - "getrandom 0.2.7", + "cfg-if", + "getrandom 0.2.8", "once_cell", "version_check", ] [[package]] name = "aho-corasick" -version = "0.7.18" +version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" dependencies = [ "memchr", ] +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + [[package]] name = "ansi_term" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" dependencies = [ - "winapi 0.3.9", + "winapi", ] [[package]] name = "arrayvec" -version = "0.4.12" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" -dependencies = [ - "nodrop", -] +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" [[package]] name = "attohttpc" @@ -396,7 +247,7 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd4da2f0d9a8eb4e0aabc162278968eaf753999aa4aed173b753f9fa8da4cb86" dependencies = [ - "http", + "http 0.1.21", "log", "url 1.7.2", ] @@ -407,18 +258,9 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", - "winapi 0.3.9", -] - -[[package]] -name = "autocfg" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" -dependencies = [ - "autocfg 1.1.0", + "winapi", ] [[package]] @@ -427,52 +269,11 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" -[[package]] -name = "awc" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e995283278dd3bf0449e7534e77184adb1570c0de8b6a50bf7c9d01ad8db8c4" -dependencies = [ - "actix-codec", - "actix-http", - "actix-service", - "base64", - "bytes", - "derive_more 0.15.0", - "futures", - "log", - "mime", - "percent-encoding 2.1.0", - "rand 0.7.3", - "serde", - "serde_json", - "serde_urlencoded 0.6.1", - "tokio-timer", -] - -[[package]] -name = "backtrace" -version = "0.3.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" -dependencies = [ - "addr2line 0.17.0", - "cc", - "cfg-if 1.0.0", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - [[package]] name = "base64" -version = "0.10.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" -dependencies = [ - "byteorder", -] +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" [[package]] name = "bitflags" @@ -481,42 +282,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] -name = "brotli-sys" -version = "0.3.2" +name = "bitflags" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4445dea95f4c2b41cde57cc9fee236ae4dbae88d8fcbdb4750fc1bb5d86aaecd" -dependencies = [ - "cc", - "libc", -] +checksum = "487f1e0fcbe47deb8b0574e646def1c903389d95241dd1bbcc6ce4a715dfc0c1" [[package]] -name = "brotli2" -version = "0.3.2" +name = "block-buffer" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cb036c3eade309815c15ddbacec5b22c4d1f3983a774ab2eac2e3e9ea85568e" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "brotli-sys", - "libc", -] - -[[package]] -name = "bstr" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" -dependencies = [ - "lazy_static", - "memchr", - "regex-automata", - "serde", + "generic-array", ] [[package]] name = "btree-range-map" -version = "0.3.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e78b7d02bdee8ad3398ef4ac5c69d5dd361b75bd4f6d17f32899c2f19cc4ab8" +checksum = "def3a109c2af9d583d0cba192ea3b8a1c62690f795d5e012d75a350322ce12e4" dependencies = [ "btree-slab", "cc-traits", @@ -526,31 +310,32 @@ dependencies = [ [[package]] name = "btree-slab" -version = "0.4.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6d5d99a446fc43a159e25be12c72f84674e1e67ec8c7402e55d1cb49205507f" +checksum = "cbd15ebdf1711bae315f668ac8865d35754613955f6473515b6aa2762ea2ec86" dependencies = [ "cc-traits", "slab", - "smallvec 1.9.0", + "smallvec", ] [[package]] name = "bumpalo" -version = "3.10.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] name = "bytehound-cli" version = "0.11.0" dependencies = [ "cli-core", - "env_logger 0.6.2", + "env_logger 0.10.0", "log", "server-core", "structopt", "tikv-jemallocator", + "tokio", ] [[package]] @@ -558,7 +343,7 @@ name = "bytehound-gather" version = "0.11.0" dependencies = [ "chrono", - "clap", + "clap 2.34.0", "cli-core", "log", ] @@ -567,31 +352,31 @@ dependencies = [ name = "bytehound-preload" version = "0.11.0" dependencies = [ - "ahash 0.8.0", + "ahash 0.8.3", "common", "fast_range_map", - "glob 0.2.11", - "goblin", - "hashbrown 0.13.1", + "glob", + "goblin 0.6.1", + "hashbrown 0.13.2", "lazy_static", "libc", "log", - "lru", + "lru 0.10.0", "mimalloc", "nwind", - "parking_lot 0.12.1", + "parking_lot", "perf_event_open", "sc", - "smallvec 1.9.0", + "smallvec", "thread-local-reentrant", "tikv-jemalloc-sys 0.4.2+5.2.1-patched.2", ] [[package]] name = "bytemuck" -version = "1.11.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5377c8865e74a160d21f29c2d40669f53286db6eab59b88540cbb12ffc8b835" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" [[package]] name = "byteorder" @@ -609,6 +394,21 @@ dependencies = [ "iovec", ] +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "bytestring" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "238e4886760d98c4f899360c834fa93e62cf7f721ac3c2da375cbdf4b8679aae" +dependencies = [ + "bytes 1.4.0", +] + [[package]] name = "cast" version = "0.3.0" @@ -617,25 +417,19 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.73" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cc-traits" -version = "0.8.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20e1d2b5991b9e82142d4e0bae811651928b98fcea218b2faca1095c8f7801e9" +checksum = "becb23f44ae9dd141d637d520b908c291bf4c5eed016ca368daa1430d54ebf4c" dependencies = [ "slab", ] -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - [[package]] name = "cfg-if" version = "1.0.0" @@ -644,15 +438,44 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.19" +version = "0.4.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" dependencies = [ - "libc", + "iana-time-zone", + "js-sys", "num-integer", "num-traits", - "time", - "winapi 0.3.9", + "time 0.1.45", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "ciborium" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369" + +[[package]] +name = "ciborium-ll" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b" +dependencies = [ + "ciborium-io", + "half", ] [[package]] @@ -663,61 +486,83 @@ checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ "ansi_term", "atty", - "bitflags", + "bitflags 1.3.2", "strsim", - "textwrap", + "textwrap 0.11.0", "unicode-width", "vec_map", ] +[[package]] +name = "clap" +version = "3.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" +dependencies = [ + "bitflags 1.3.2", + "clap_lex", + "indexmap", + "textwrap 0.16.0", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "cli-core" version = "0.11.0" dependencies = [ - "ahash 0.7.6", - "bitflags", + "ahash 0.8.3", + "bitflags 2.0.2", "byteorder", "chrono", "colorgrad", "common", - "cpp_demangle 0.2.16", - "crossbeam-channel 0.5.6", + "cpp_demangle 0.4.0", + "crossbeam-channel", "ctrlc", - "derive_more 0.99.17", + "derive_more", "fast_range_map", - "goblin", + "goblin 0.6.1", "inferno", "lazy_static", "libc", "log", - "lru", + "lru 0.10.0", "lz4-compress", "nwind", - "parking_lot 0.12.1", + "parking_lot", "plotters", "quickcheck", "rayon", "regex", "rhai", "serde_json", - "smallvec 1.9.0", + "smallvec", "string-interner", ] [[package]] -name = "cloudabi" -version = "0.0.3" +name = "codespan-reporting" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" dependencies = [ - "bitflags", + "termcolor", + "unicode-width", ] [[package]] name = "colorgrad" -version = "0.4.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fe6e84910b5d61e01f64515e4bef312cb7523cac732b776874a6fc41a1b722d" +checksum = "6a5f405d474b9d05e0a093d3120e77e9bf26461b57a84b40aa2a221ac5617fb6" dependencies = [ "csscolorparser", ] @@ -726,35 +571,13 @@ dependencies = [ name = "common" version = "0.11.0" dependencies = [ - "bitflags", + "bitflags 2.0.2", "byteorder", "libc", "lz4-compress", "speedy", ] -[[package]] -name = "const-random" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f590d95d011aa80b063ffe3253422ed5aa462af4e9867d43ce8337562bac77c4" -dependencies = [ - "const-random-macro", - "proc-macro-hack", -] - -[[package]] -name = "const-random-macro" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "615f6e27d000a2bffbc7f2f6a8669179378fa27ee4d0a509e985dfc0a7defb40" -dependencies = [ - "getrandom 0.2.7", - "lazy_static", - "proc-macro-hack", - "tiny-keccak", -] - [[package]] name = "convert_case" version = "0.4.0" @@ -762,50 +585,50 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" [[package]] -name = "copyless" -version = "0.1.5" +name = "core-foundation-sys" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2df960f5d869b2dd8532793fde43eb5427cceb126c929747a26823ab0eeb536" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" [[package]] name = "cpp_demangle" -version = "0.2.16" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c924384107361ca729c7d46b9134151b9a955ce99a773784f2777498e8552d" +checksum = "eeaa953eaad386a53111e47172c2fedba671e5684c8dd601a5f474f4f118710f" dependencies = [ - "cfg-if 0.1.10", - "glob 0.3.0", + "cfg-if", ] [[package]] name = "cpp_demangle" -version = "0.3.5" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeaa953eaad386a53111e47172c2fedba671e5684c8dd601a5f474f4f118710f" +checksum = "b446fd40bcc17eddd6a4a78f24315eb90afdb3334999ddfd4909985c47722442" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] -name = "crc32fast" -version = "1.3.2" +name = "cpufeatures" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181" dependencies = [ - "cfg-if 1.0.0", + "libc", ] [[package]] name = "criterion" -version = "0.3.6" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f" +checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb" dependencies = [ + "anes", "atty", "cast", - "clap", + "ciborium", + "clap 3.2.23", "criterion-plot", - "csv", "itertools", "lazy_static", "num-traits", @@ -814,7 +637,6 @@ dependencies = [ "rayon", "regex", "serde", - "serde_cbor", "serde_derive", "serde_json", "tinytemplate", @@ -823,9 +645,9 @@ dependencies = [ [[package]] name = "criterion-plot" -version = "0.4.5" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" dependencies = [ "cast", "itertools", @@ -833,116 +655,66 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8ec7fcd21571dc78f96cc96243cab8d8f035247c3efd16c687be154c3fa9efa" -dependencies = [ - "crossbeam-utils 0.6.6", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" +checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" dependencies = [ - "cfg-if 1.0.0", - "crossbeam-utils 0.8.11", + "cfg-if", + "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "crossbeam-epoch", - "crossbeam-utils 0.8.11", + "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" -version = "0.9.10" +version = "0.9.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1" +checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" dependencies = [ - "autocfg 1.1.0", - "cfg-if 1.0.0", - "crossbeam-utils 0.8.11", + "autocfg", + "cfg-if", + "crossbeam-utils", "memoffset", - "once_cell", "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" -dependencies = [ - "cfg-if 0.1.10", - "lazy_static", -] - -[[package]] -name = "crossbeam-utils" -version = "0.7.2" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" dependencies = [ - "autocfg 1.1.0", - "cfg-if 0.1.10", - "lazy_static", + "cfg-if", ] [[package]] -name = "crossbeam-utils" -version = "0.8.11" +name = "crypto-common" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "cfg-if 1.0.0", - "once_cell", + "generic-array", + "typenum", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "csscolorparser" -version = "0.4.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b47d7b15b26d6d53076f0096b9f07dc27b329eeee0c3daa9cb91cd7e4856e638" +checksum = "eb2a7d3066da2de787b7f032c736763eb7ae5d355f81a68bab2675a96008b0bf" dependencies = [ "phf", ] -[[package]] -name = "csv" -version = "1.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" -dependencies = [ - "bstr", - "csv-core", - "itoa 0.4.8", - "ryu", - "serde", -] - -[[package]] -name = "csv-core" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" -dependencies = [ - "memchr", -] - [[package]] name = "ctrlc" version = "3.2.2" @@ -950,7 +722,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b37feaa84e6861e00a1f5e5aa8da3ee56d605c9992d33e082786754828e20865" dependencies = [ "nix", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -960,29 +732,47 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" [[package]] -name = "derive_more" -version = "0.14.1" +name = "cxx" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d944ac6003ed268757ef1ee686753b57efc5fcf0ebe7b64c9fc81e7e32ff839" +checksum = "a9c00419335c41018365ddf7e4d5f1c12ee3659ddcf3e01974650ba1de73d038" dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "rustc_version 0.2.3", - "syn 0.15.44", + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", ] [[package]] -name = "derive_more" -version = "0.15.0" +name = "cxx-build" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a141330240c921ec6d074a3e188a7c7ef95668bb95e7d44fa0e5778ec2a7afe" +checksum = "fb8307ad413a98fff033c8545ecf133e3257747b3bae935e7602aab8aa92d4ca" dependencies = [ - "lazy_static", - "proc-macro2 0.4.30", - "quote 0.6.13", - "regex", - "rustc_version 0.2.3", - "syn 0.15.44", + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2 1.0.53", + "quote 1.0.26", + "scratch", + "syn 2.0.10", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edc52e2eb08915cb12596d29d55f0b5384f00d697a646dbd269b6ecb0fbd9d31" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "631569015d0d8d54e6c241733f944042623ab6df7bc3be7466874b05fcdb1c5f" +dependencies = [ + "proc-macro2 1.0.53", + "quote 1.0.26", + "syn 2.0.10", ] [[package]] @@ -992,65 +782,58 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "convert_case", - "proc-macro2 1.0.41", - "quote 1.0.20", + "proc-macro2 1.0.53", + "quote 1.0.26", "rustc_version 0.4.0", - "syn 1.0.98", + "syn 1.0.109", ] [[package]] -name = "dtoa" -version = "0.4.8" +name = "digest" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer", + "crypto-common", +] [[package]] name = "either" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "encoding_rs" -version = "0.8.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" -dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "enum-as-inner" -version = "0.2.1" +version = "0.8.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d58266c97445680766be408285e798d3401c6d4c378ec5552e78737e681e37d" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "syn 0.15.44", + "cfg-if", ] [[package]] name = "env_logger" -version = "0.6.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" dependencies = [ - "atty", - "humantime", "log", "regex", - "termcolor", ] [[package]] name = "env_logger" -version = "0.7.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" dependencies = [ + "humantime", + "is-terminal", "log", "regex", + "termcolor", ] [[package]] @@ -1061,7 +844,7 @@ checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" dependencies = [ "errno-dragonfly", "libc", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -1074,28 +857,6 @@ dependencies = [ "libc", ] -[[package]] -name = "failure" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" -dependencies = [ - "backtrace", - "failure_derive", -] - -[[package]] -name = "failure_derive" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" -dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.20", - "syn 1.0.98", - "synstructure", -] - [[package]] name = "fallible-iterator" version = "0.2.0" @@ -1108,30 +869,20 @@ version = "0.1.0" dependencies = [ "btree-range-map", "criterion", - "hashbrown 0.13.1", + "hashbrown 0.13.2", "oorandom", "rangemap", ] [[package]] name = "fastrand" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" dependencies = [ "instant", ] -[[package]] -name = "flate2" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - [[package]] name = "fnv" version = "1.0.7" @@ -1140,47 +891,117 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" dependencies = [ - "matches", - "percent-encoding 2.1.0", + "percent-encoding 2.2.0", ] [[package]] name = "fs_extra" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" [[package]] -name = "fuchsia-cprng" -version = "0.1.1" +name = "futures" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" +checksum = "531ac96c6ff5fd7c62263c5e3c67a603af4fcaee2e1a0ae5565ba3a11e69e549" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] [[package]] -name = "fuchsia-zircon" -version = "0.3.3" +name = "futures-channel" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +checksum = "164713a5a0dcc3e7b4b1ed7d3b433cabc18025386f9339346e8daf15963cf7ac" dependencies = [ - "bitflags", - "fuchsia-zircon-sys", + "futures-core", + "futures-sink", ] [[package]] -name = "fuchsia-zircon-sys" -version = "0.3.3" +name = "futures-core" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +checksum = "86d7a0c1aa76363dac491de0ee99faf6941128376f1cf96f07db7603b7de69dd" [[package]] -name = "futures" -version = "0.1.31" +name = "futures-executor" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1997dd9df74cdac935c76252744c1ed5794fac083242ea4fe77ef3ed60ba0f83" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d422fa3cbe3b40dca574ab087abb5bc98258ea57eea3fd6f1fa7162c778b91" + +[[package]] +name = "futures-macro" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eb14ed937631bd8b8b8977f2c198443447a8355b6e3ca599f38c975e5a963b6" +dependencies = [ + "proc-macro2 1.0.53", + "quote 1.0.26", + "syn 1.0.109", +] + +[[package]] +name = "futures-sink" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2" + +[[package]] +name = "futures-task" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd65540d33b37b16542a0438c12e6aeead10d4ac5d05bd3f805b8f35ab592879" + +[[package]] +name = "futures-util" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" +checksum = "3ef6b17e481503ec85211fed8f39d1970f128935ca1f814cd32ac4a6842e84ab" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] [[package]] name = "getrandom" @@ -1188,18 +1009,18 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "wasi 0.9.0+wasi-snapshot-preview1", ] [[package]] name = "getrandom" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "wasi 0.11.0+wasi-snapshot-preview1", ] @@ -1214,51 +1035,51 @@ dependencies = [ "stable_deref_trait", ] -[[package]] -name = "gimli" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" - [[package]] name = "glob" -version = "0.2.11" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] -name = "glob" -version = "0.3.0" +name = "goblin" +version = "0.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +checksum = "e3fa261d919c1ae9d1e4533c4a2f99e10938603c4208d56c05bec7a872b661b0" +dependencies = [ + "log", + "plain", + "scroll 0.9.2", +] [[package]] name = "goblin" -version = "0.0.24" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3fa261d919c1ae9d1e4533c4a2f99e10938603c4208d56c05bec7a872b661b0" +checksum = "0d6b4de4a8eb6c46a8c77e1d3be942cb9a8bf073c22374578e5ba4b08ed0ff68" dependencies = [ "log", "plain", - "scroll", + "scroll 0.11.0", ] [[package]] name = "h2" -version = "0.1.26" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5b34c246847f938a410a03c5458c7fee2274436675e76d8b903c08efc29c462" +checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d" dependencies = [ - "byteorder", - "bytes", + "bytes 1.4.0", "fnv", - "futures", - "http", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.9", "indexmap", - "log", "slab", - "string", - "tokio-io", + "tokio", + "tokio-util", + "tracing", ] [[package]] @@ -1267,22 +1088,6 @@ version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" -[[package]] -name = "hashbrown" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29fba9abe4742d586dfd0c06ae4f7e73a1c2d86b856933509b269d82cdf06e18" - -[[package]] -name = "hashbrown" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e6073d0ca812575946eb5f35ff68dbe519907b25c42530389ff946dc84c6ead" -dependencies = [ - "ahash 0.2.19", - "autocfg 0.1.8", -] - [[package]] name = "hashbrown" version = "0.11.2" @@ -1300,11 +1105,11 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ff8ae62cd3a9102e5637afc8452c55acf3844001bd5374e0b0bd7b6616c038" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash 0.8.0", + "ahash 0.8.3", ] [[package]] @@ -1326,40 +1131,82 @@ dependencies = [ ] [[package]] -name = "hostname" -version = "0.3.1" +name = "hermit-abi" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" dependencies = [ "libc", - "match_cfg", - "winapi 0.3.9", ] +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + [[package]] name = "http" version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6ccf5ede3a895d8856620237b2f02972c1bbc78d2965ad7fe8838d4a0ed41f0" dependencies = [ - "bytes", + "bytes 0.4.12", + "fnv", + "itoa 0.4.8", +] + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes 1.4.0", "fnv", - "itoa 0.4.8", + "itoa 1.0.6", ] [[package]] name = "httparse" -version = "1.7.1" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "humantime" -version = "1.3.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "iana-time-zone" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c17cc76786e99f8d2f055c11159e7f0091c42474dcc3189fbab96072e873e6d" dependencies = [ - "quick-error", + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", ] [[package]] @@ -1375,36 +1222,36 @@ dependencies = [ [[package]] name = "idna" -version = "0.2.3" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" dependencies = [ - "matches", "unicode-bidi", "unicode-normalization", ] [[package]] name = "indexmap" -version = "1.9.1" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ - "autocfg 1.1.0", + "autocfg", "hashbrown 0.12.3", ] [[package]] name = "inferno" -version = "0.9.9" +version = "0.11.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b2a71c56e4c218f2a1d36bc5177cbfdedf89697ac68610ac3c8452cde152231" +checksum = "2fb7c1b80a1dfa604bb4a649a5c5aeef3d913f7c520cb42b40e534e8a61bcdfc" dependencies = [ - "ahash 0.3.8", - "itoa 0.4.8", - "lazy_static", + "ahash 0.8.3", + "is-terminal", + "itoa 1.0.6", "log", "num-format", + "once_cell", "quick-xml", "rgb", "str_stack", @@ -1416,7 +1263,7 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -1430,6 +1277,17 @@ dependencies = [ "serde_json", ] +[[package]] +name = "io-lifetimes" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb" +dependencies = [ + "hermit-abi 0.3.1", + "libc", + "windows-sys 0.45.0", +] + [[package]] name = "iovec" version = "0.1.4" @@ -1440,22 +1298,22 @@ dependencies = [ ] [[package]] -name = "ipconfig" -version = "0.2.2" +name = "is-terminal" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7e2f18aece9709094573a9f24f483c4f65caa4298e2f7ae1b71cc65d853fad7" +checksum = "8687c819457e979cc940d09cb16e42a1bf70aa6b60a549de6d3a62a0ee90c69e" dependencies = [ - "socket2", - "widestring", - "winapi 0.3.9", - "winreg", + "hermit-abi 0.3.1", + "io-lifetimes", + "rustix", + "windows-sys 0.45.0", ] [[package]] name = "itertools" -version = "0.10.4" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8bf247779e67a9082a4790b45e71ac7cfd1321331a5c856a74a9faebdab78d0" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] @@ -1468,34 +1326,24 @@ checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" [[package]] name = "itoa" -version = "1.0.2" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "js-sys" -version = "0.3.58" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" dependencies = [ "wasm-bindgen", ] -[[package]] -name = "kernel32-sys" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -dependencies = [ - "winapi 0.2.8", - "winapi-build", -] - [[package]] name = "language-tags" -version = "0.2.2" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" +checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" [[package]] name = "lazy_static" @@ -1505,9 +1353,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.126" +version = "0.2.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" [[package]] name = "libmimalloc-sys" @@ -1518,36 +1366,45 @@ dependencies = [ ] [[package]] -name = "linked-hash-map" -version = "0.5.6" +name = "link-cplusplus" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +dependencies = [ + "cc", +] [[package]] -name = "lock_api" -version = "0.2.0" +name = "linux-raw-sys" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed946d4529956a20f2d63ebe1b69996d5a2137c91913fe3ebbeff957f5bca7ff" -dependencies = [ - "scopeguard", -] +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" [[package]] -name = "lock_api" -version = "0.3.4" +name = "local-channel" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" +checksum = "7f303ec0e94c6c54447f84f3b0ef7af769858a9c4ef56ef2a986d3dcd4c3fc9c" dependencies = [ - "scopeguard", + "futures-core", + "futures-sink", + "futures-util", + "local-waker", ] +[[package]] +name = "local-waker" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e34f76eb3611940e0e7d53a9aaa4e6a3151f69541a282fd0dad5571420c53ff1" + [[package]] name = "lock_api" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" dependencies = [ - "autocfg 1.1.0", + "autocfg", "scopeguard", ] @@ -1557,7 +1414,7 @@ version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -1570,12 +1427,12 @@ dependencies = [ ] [[package]] -name = "lru-cache" -version = "0.1.2" +name = "lru" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +checksum = "03f1160296536f10c833a82dca22267d5486734230d47bf00bf435885814ba1e" dependencies = [ - "linked-hash-map", + "hashbrown 0.13.2", ] [[package]] @@ -1586,23 +1443,11 @@ dependencies = [ "quick-error", ] -[[package]] -name = "match_cfg" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" - [[package]] name = "matches" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" - -[[package]] -name = "maybe-uninit" -version = "2.0.0" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "md5" @@ -1623,16 +1468,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" dependencies = [ "libc", - "winapi 0.3.9", + "winapi", ] [[package]] name = "memoffset" -version = "0.6.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" dependencies = [ - "autocfg 1.1.0", + "autocfg", ] [[package]] @@ -1644,70 +1489,20 @@ dependencies = [ [[package]] name = "mime" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" - -[[package]] -name = "miniz_oxide" -version = "0.5.3" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" -dependencies = [ - "adler", -] +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mio" -version = "0.6.23" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" dependencies = [ - "cfg-if 0.1.10", - "fuchsia-zircon", - "fuchsia-zircon-sys", - "iovec", - "kernel32-sys", "libc", "log", - "miow", - "net2", - "slab", - "winapi 0.2.8", -] - -[[package]] -name = "mio-uds" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" -dependencies = [ - "iovec", - "libc", - "mio", -] - -[[package]] -name = "miow" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" -dependencies = [ - "kernel32-sys", - "net2", - "winapi 0.2.8", - "ws2_32-sys", -] - -[[package]] -name = "net2" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" -dependencies = [ - "cfg-if 0.1.10", - "libc", - "winapi 0.3.9", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.45.0", ] [[package]] @@ -1716,25 +1511,19 @@ version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc" dependencies = [ - "bitflags", - "cfg-if 1.0.0", + "bitflags 1.3.2", + "cfg-if", "libc", ] -[[package]] -name = "nodrop" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" - [[package]] name = "num-format" -version = "0.4.0" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bafe4179722c2894288ee77a9f044f02811c86af699344c498b0840c698a2465" +checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" dependencies = [ "arrayvec", - "itoa 0.4.8", + "itoa 1.0.6", ] [[package]] @@ -1743,7 +1532,7 @@ version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ - "autocfg 1.1.0", + "autocfg", "num-traits", ] @@ -1753,16 +1542,16 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ - "autocfg 1.1.0", + "autocfg", ] [[package]] name = "num_cpus" -version = "1.13.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ - "hermit-abi", + "hermit-abi 0.2.6", "libc", ] @@ -1771,37 +1560,28 @@ name = "nwind" version = "0.1.0" source = "git+https://github.com/koute/not-perf.git?rev=911723c#911723cf9099cc0f9e45b921f045117535cd1f06" dependencies = [ - "addr2line 0.16.0", + "addr2line", "byteorder", "cc", "cpp_demangle 0.3.5", - "gimli 0.25.0", - "goblin", + "gimli", + "goblin 0.0.24", "libc", "log", - "lru", + "lru 0.6.6", "memmap", "proc-maps", "rustc-demangle", - "scroll", + "scroll 0.9.2", "speedy", "thread-local-reentrant", ] -[[package]] -name = "object" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" -dependencies = [ - "memchr", -] - [[package]] name = "once_cell" -version = "1.13.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "oorandom" @@ -1810,26 +1590,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] -name = "parking_lot" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7767817701cce701d5585b9c4db3cdd02086398322c1d7e8bf5094a96a2ce7" -dependencies = [ - "lock_api 0.2.0", - "parking_lot_core 0.5.0", - "rustc_version 0.2.3", -] - -[[package]] -name = "parking_lot" -version = "0.9.0" +name = "os_str_bytes" +version = "6.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" -dependencies = [ - "lock_api 0.3.4", - "parking_lot_core 0.6.2", - "rustc_version 0.2.3", -] +checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" [[package]] name = "parking_lot" @@ -1837,53 +1601,28 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ - "lock_api 0.4.7", - "parking_lot_core 0.9.3", -] - -[[package]] -name = "parking_lot_core" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb88cb1cb3790baa6776844f968fea3be44956cf184fa1be5a03341f5491278c" -dependencies = [ - "cfg-if 0.1.10", - "cloudabi", - "libc", - "rand 0.6.5", - "redox_syscall 0.1.57", - "rustc_version 0.2.3", - "smallvec 0.6.14", - "winapi 0.3.9", + "lock_api", + "parking_lot_core", ] [[package]] name = "parking_lot_core" -version = "0.6.2" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ - "cfg-if 0.1.10", - "cloudabi", + "cfg-if", "libc", - "redox_syscall 0.1.57", - "rustc_version 0.2.3", - "smallvec 0.6.14", - "winapi 0.3.9", + "redox_syscall", + "smallvec", + "windows-sys 0.45.0", ] [[package]] -name = "parking_lot_core" -version = "0.9.3" +name = "paste" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "redox_syscall 0.2.15", - "smallvec 1.9.0", - "windows-sys", -] +checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" [[package]] name = "percent-encoding" @@ -1893,9 +1632,9 @@ checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" [[package]] name = "percent-encoding" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "perf_event_open" @@ -1905,53 +1644,63 @@ dependencies = [ "byteorder", "libc", "log", - "parking_lot 0.12.1", + "parking_lot", ] [[package]] name = "phf" -version = "0.8.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" +checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c" dependencies = [ "phf_macros", "phf_shared", - "proc-macro-hack", ] [[package]] name = "phf_generator" -version = "0.8.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" +checksum = "b1181c94580fa345f50f19d738aaa39c0ed30a600d95cb2d3e23f94266f14fbf" dependencies = [ "phf_shared", - "rand 0.7.3", + "rand 0.8.5", ] [[package]] name = "phf_macros" -version = "0.8.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" +checksum = "92aacdc5f16768709a569e913f7451034034178b05bdc8acda226659a3dccc66" dependencies = [ "phf_generator", "phf_shared", - "proc-macro-hack", - "proc-macro2 1.0.41", - "quote 1.0.20", - "syn 1.0.98", + "proc-macro2 1.0.53", + "quote 1.0.26", + "syn 1.0.109", ] [[package]] name = "phf_shared" -version = "0.8.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676" dependencies = [ "siphasher", ] +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "plain" version = "0.2.3" @@ -1960,9 +1709,9 @@ checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" [[package]] name = "plotters" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9428003b84df1496fb9d6eeee9c5f8145cb41ca375eb0dad204328888832811f" +checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97" dependencies = [ "num-traits", "plotters-backend", @@ -1979,24 +1728,42 @@ checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142" [[package]] name = "plotters-svg" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0918736323d1baff32ee0eade54984f6f201ad7e97d5cfb5d6ab4a358529615" +checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f" dependencies = [ "plotters-backend", ] [[package]] name = "ppv-lite86" -version = "0.2.16" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro-error" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2 1.0.53", + "quote 1.0.26", + "syn 1.0.109", + "version_check", +] [[package]] -name = "proc-macro-hack" -version = "0.5.19" +name = "proc-macro-error-attr" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2 1.0.53", + "quote 1.0.26", + "version_check", +] [[package]] name = "proc-macro2" @@ -2004,14 +1771,14 @@ version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" dependencies = [ - "unicode-xid 0.1.0", + "unicode-xid", ] [[package]] name = "proc-macro2" -version = "1.0.41" +version = "1.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdcc2916cde080c1876ff40292a396541241fe0072ef928cd76582e9ea5d60d2" +checksum = "ba466839c78239c09faf015484e5cc04860f88242cff4d03eb038f04b4699b73" dependencies = [ "unicode-ident", ] @@ -2023,15 +1790,15 @@ source = "git+https://github.com/koute/not-perf.git?rev=911723c#911723cf9099cc0f [[package]] name = "quick-error" -version = "1.2.3" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quick-xml" -version = "0.18.1" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cc440ee4802a86e357165021e3e255a9143724da31db1e2ea540214c96a0f82" +checksum = "7f50b1c63b38611e7d4d7f68b82d3ad0cc71a2ad2e7f61fc10f1328d917c93cd" dependencies = [ "memchr", ] @@ -2059,30 +1826,11 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" -dependencies = [ - "proc-macro2 1.0.41", -] - -[[package]] -name = "rand" -version = "0.6.5" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ - "autocfg 0.1.8", - "libc", - "rand_chacha 0.1.1", - "rand_core 0.4.2", - "rand_hc 0.1.0", - "rand_isaac", - "rand_jitter", - "rand_os", - "rand_pcg 0.1.2", - "rand_xorshift", - "winapi 0.3.9", + "proc-macro2 1.0.53", ] [[package]] @@ -2095,18 +1843,18 @@ dependencies = [ "libc", "rand_chacha 0.2.2", "rand_core 0.5.1", - "rand_hc 0.2.0", - "rand_pcg 0.2.1", + "rand_hc", ] [[package]] -name = "rand_chacha" -version = "0.1.1" +name = "rand" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ - "autocfg 0.1.8", - "rand_core 0.3.1", + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", ] [[package]] @@ -2120,20 +1868,15 @@ dependencies = [ ] [[package]] -name = "rand_core" +name = "rand_chacha" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ - "rand_core 0.4.2", + "ppv-lite86", + "rand_core 0.6.4", ] -[[package]] -name = "rand_core" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" - [[package]] name = "rand_core" version = "0.5.1" @@ -2144,12 +1887,12 @@ dependencies = [ ] [[package]] -name = "rand_hc" -version = "0.1.0" +name = "rand_core" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "rand_core 0.3.1", + "getrandom 0.2.8", ] [[package]] @@ -2161,175 +1904,71 @@ dependencies = [ "rand_core 0.5.1", ] -[[package]] -name = "rand_isaac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rand_jitter" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" -dependencies = [ - "libc", - "rand_core 0.4.2", - "winapi 0.3.9", -] - -[[package]] -name = "rand_os" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" -dependencies = [ - "cloudabi", - "fuchsia-cprng", - "libc", - "rand_core 0.4.2", - "rdrand", - "winapi 0.3.9", -] - -[[package]] -name = "rand_pcg" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" -dependencies = [ - "autocfg 0.1.8", - "rand_core 0.4.2", -] - -[[package]] -name = "rand_pcg" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "rand_xorshift" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" -dependencies = [ - "rand_core 0.3.1", -] - [[package]] name = "range-traits" -version = "0.1.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4473d273e456fd55eb6539c391f22c6e501fff9a800e84431ba68b26001c0a16" +checksum = "9ea26694da042b4016873b8c55515348f40d77281f239ade1f2ede3afb8da92c" [[package]] name = "rangemap" -version = "1.0.3" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea2d559b3970fe7aa56ce7432a3702ff4b20a57b543ae08b4850ee629353ea6" +checksum = "8b9283c6b06096b47afc7109834fdedab891175bb5241ee5d4f7d2546549f263" [[package]] name = "rayon" -version = "1.5.3" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" dependencies = [ - "autocfg 1.1.0", - "crossbeam-deque", "either", "rayon-core", ] [[package]] name = "rayon-core" -version = "1.9.3" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" dependencies = [ - "crossbeam-channel 0.5.6", + "crossbeam-channel", "crossbeam-deque", - "crossbeam-utils 0.8.11", + "crossbeam-utils", "num_cpus", ] -[[package]] -name = "rdrand" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "redox_syscall" -version = "0.1.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" - [[package]] name = "redox_syscall" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534cfe58d6a18cc17120fbf4635d53d14691c1fe4d951064df9bd326178d7d5a" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] name = "regex" -version = "1.6.0" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" - [[package]] name = "regex-syntax" -version = "0.6.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" - -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi 0.3.9", -] - -[[package]] -name = "resolv-conf" -version = "0.6.3" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11834e137f3b14e309437a8276714eed3a80d1ef894869e510f2c0c0b98b9f4a" -dependencies = [ - "hostname", - "quick-error", -] +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "rgb" -version = "0.8.33" +version = "0.8.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3b221de559e4a29df3b957eec92bc0de6bc8eaf6ca9cfed43e5e1d67ff65a34" +checksum = "20ec2d3e3fc7a92ced357df9cebd5a10b6fb2aa1ee797bf7e9ce2f17dffc8f59" dependencies = [ "bytemuck", ] @@ -2341,11 +1980,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f8a1b0ce6aba109eb3628f36db9c1818c1d396bd5a56449d5203e96e4b713a7" dependencies = [ "ahash 0.7.6", - "bitflags", + "bitflags 1.3.2", "instant", "num-traits", "rhai_codegen", - "smallvec 1.9.0", + "smallvec", "smartstring", ] @@ -2355,16 +1994,16 @@ version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75a39bc2aa9258b282ee5518dac493491a9c4c11a6d7361b9d2644c922fc6488" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.20", - "syn 1.0.98", + "proc-macro2 1.0.53", + "quote 1.0.26", + "syn 1.0.109", ] [[package]] name = "rustc-demangle" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" +checksum = "d4a36c42d1873f9a77c53bde094f9664d9891bc604a45b4798fd2c389ed12e5b" [[package]] name = "rustc_version" @@ -2381,14 +2020,28 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.13", + "semver 1.0.17", +] + +[[package]] +name = "rustix" +version = "0.36.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db4165c9963ab29e422d6c26fbc1d37f15bace6b2810221f9d925023480fcf0e" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.45.0", ] [[package]] name = "ryu" -version = "1.0.10" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "same-file" @@ -2411,6 +2064,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "scratch" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" + [[package]] name = "scroll" version = "0.9.2" @@ -2418,7 +2077,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f84d114ef17fd144153d608fba7c446b0145d038985e7a8cc5d08bb0ce20383" dependencies = [ "rustc_version 0.2.3", - "scroll_derive", + "scroll_derive 0.9.5", +] + +[[package]] +name = "scroll" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da" +dependencies = [ + "scroll_derive 0.11.0", ] [[package]] @@ -2432,11 +2100,22 @@ dependencies = [ "syn 0.15.44", ] +[[package]] +name = "scroll_derive" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdbda6ac5cd1321e724fa9cee216f3a61885889b896f073b8f82322789c5250e" +dependencies = [ + "proc-macro2 1.0.53", + "quote 1.0.26", + "syn 1.0.109", +] + [[package]] name = "semalock" -version = "0.2.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dbc7634a3320cdf32ef8146196231f42fe581994e64ae9e6493b9e02a15f5a9" +checksum = "a89f260f777c6feebacabad0790eaf02b0cdcdb0b5de220630911b5dd59142ca" dependencies = [ "errno", "libc", @@ -2454,9 +2133,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.13" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f6841e709003d68bb2deee8c343572bf446003ec20a583e76f7b15cebf3711" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" [[package]] name = "semver-parser" @@ -2466,114 +2145,88 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.140" +version = "1.0.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc855a42c7967b7c369eb5860f7164ef1f6f81c20c7cc1141f2a604e18723b03" +checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9" dependencies = [ "serde_derive", ] -[[package]] -name = "serde_cbor" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" -dependencies = [ - "half", - "serde", -] - [[package]] name = "serde_derive" -version = "1.0.140" +version = "1.0.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f2122636b9fe3b81f1cb25099fcf2d3f542cdb1d45940d56c713158884a05da" +checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.20", - "syn 1.0.98", + "proc-macro2 1.0.53", + "quote 1.0.26", + "syn 2.0.10", ] [[package]] name = "serde_json" -version = "1.0.82" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7" +checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" dependencies = [ - "itoa 1.0.2", + "itoa 1.0.6", "ryu", "serde", ] [[package]] name = "serde_urlencoded" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "642dd69105886af2efd227f75a520ec9b44a820d65bc133a9131f7d229fd165a" -dependencies = [ - "dtoa", - "itoa 0.4.8", - "serde", - "url 1.7.2", -] - -[[package]] -name = "serde_urlencoded" -version = "0.6.1" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ - "dtoa", - "itoa 0.4.8", + "form_urlencoded", + "itoa 1.0.6", + "ryu", "serde", - "url 2.2.2", ] [[package]] name = "server-core" version = "0.11.0" dependencies = [ - "actix", "actix-cors", "actix-web", - "ahash 0.7.6", - "bytes", + "ahash 0.8.3", + "bytes 1.4.0", "cli-core", "common", "futures", + "futures-core", "log", - "lru", + "lru 0.10.0", "md5", - "parking_lot 0.12.1", + "parking_lot", "rayon", "regex", "semalock", "serde", "serde_derive", "serde_json", - "serde_urlencoded 0.5.5", + "serde_urlencoded", ] [[package]] name = "sha1" -version = "0.6.1" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ - "sha1_smol", + "cfg-if", + "cpufeatures", + "digest", ] -[[package]] -name = "sha1_smol" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" - [[package]] name = "signal-hook-registry" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ "libc", ] @@ -2586,27 +2239,18 @@ checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" [[package]] name = "slab" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" -dependencies = [ - "autocfg 1.1.0", -] - -[[package]] -name = "smallvec" -version = "0.6.14" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ - "maybe-uninit", + "autocfg", ] [[package]] name = "smallvec" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "smartstring" @@ -2614,20 +2258,19 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29" dependencies = [ - "autocfg 1.1.0", + "autocfg", "static_assertions", "version_check", ] [[package]] name = "socket2" -version = "0.3.19" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ - "cfg-if 1.0.0", "libc", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -2645,9 +2288,9 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00d56cb212a4b09de6561bac41bc36dc810fd41faf24b0a8aa1374062546af0a" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.20", - "syn 1.0.98", + "proc-macro2 1.0.53", + "quote 1.0.26", + "syn 1.0.109", ] [[package]] @@ -2668,15 +2311,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9091b6114800a5f2141aee1d1b9d6ca3592ac062dc5decb3764ec5895a47b4eb" -[[package]] -name = "string" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d" -dependencies = [ - "bytes", -] - [[package]] name = "string-interner" version = "0.7.1" @@ -2691,24 +2325,26 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "structopt" -version = "0.2.18" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16c2cdbf9cc375f15d1b4141bc48aeef444806655cd0e904207edc8d68d86ed7" +checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" dependencies = [ - "clap", + "clap 2.34.0", + "lazy_static", "structopt-derive", ] [[package]] name = "structopt-derive" -version = "0.2.18" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53010261a84b37689f9ed7d395165029f9cc7abb9f56bbfe86bee2597ed25107" +checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" dependencies = [ "heck", - "proc-macro2 0.4.30", - "quote 0.6.13", - "syn 0.15.44", + "proc-macro-error", + "proc-macro2 1.0.53", + "quote 1.0.26", + "syn 1.0.109", ] [[package]] @@ -2719,44 +2355,42 @@ checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" dependencies = [ "proc-macro2 0.4.30", "quote 0.6.13", - "unicode-xid 0.1.0", + "unicode-xid", ] [[package]] name = "syn" -version = "1.0.98" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.20", + "proc-macro2 1.0.53", + "quote 1.0.26", "unicode-ident", ] [[package]] -name = "synstructure" -version = "0.12.6" +name = "syn" +version = "2.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +checksum = "5aad1363ed6d37b84299588d62d3a7d95b5a5c2d9aad5c85609fda12afaa1f40" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.20", - "syn 1.0.98", - "unicode-xid 0.2.3", + "proc-macro2 1.0.53", + "quote 1.0.26", + "unicode-ident", ] [[package]] name = "tempfile" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "fastrand", - "libc", - "redox_syscall 0.2.15", - "remove_dir_all", - "winapi 0.3.9", + "redox_syscall", + "rustix", + "windows-sys 0.42.0", ] [[package]] @@ -2777,6 +2411,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "textwrap" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" + [[package]] name = "thread-local-reentrant" version = "0.1.0" @@ -2785,15 +2425,6 @@ dependencies = [ "libc", ] -[[package]] -name = "threadpool" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" -dependencies = [ - "num_cpus", -] - [[package]] name = "tikv-jemalloc-sys" version = "0.4.2+5.2.1-patched.2" @@ -2826,239 +2457,156 @@ dependencies = [ [[package]] name = "time" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" dependencies = [ "libc", "wasi 0.10.0+wasi-snapshot-preview1", - "winapi 0.3.9", -] - -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", + "winapi", ] [[package]] -name = "tinytemplate" -version = "1.2.1" +name = "time" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" dependencies = [ + "itoa 1.0.6", "serde", - "serde_json", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", + "time-core", + "time-macros", ] [[package]] -name = "tinyvec_macros" +name = "time-core" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" - -[[package]] -name = "tokio-codec" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25b2998660ba0e70d18684de5d06b70b70a3a747469af9dea7618cc59e75976b" -dependencies = [ - "bytes", - "futures", - "tokio-io", -] +checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" [[package]] -name = "tokio-current-thread" -version = "0.1.7" +name = "time-macros" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e" +checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36" dependencies = [ - "futures", - "tokio-executor", + "time-core", ] [[package]] -name = "tokio-executor" -version = "0.1.10" +name = "tinytemplate" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" dependencies = [ - "crossbeam-utils 0.7.2", - "futures", + "serde", + "serde_json", ] [[package]] -name = "tokio-io" -version = "0.1.13" +name = "tinyvec" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ - "bytes", - "futures", - "log", + "tinyvec_macros", ] [[package]] -name = "tokio-reactor" -version = "0.1.12" +name = "tinyvec_macros" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351" -dependencies = [ - "crossbeam-utils 0.7.2", - "futures", - "lazy_static", - "log", - "mio", - "num_cpus", - "parking_lot 0.9.0", - "slab", - "tokio-executor", - "tokio-io", - "tokio-sync", -] +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] -name = "tokio-signal" -version = "0.2.9" +name = "tokio" +version = "1.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c34c6e548f101053321cba3da7cbb87a610b85555884c41b07da2eb91aff12" +checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" dependencies = [ - "futures", + "autocfg", + "bytes 1.4.0", "libc", + "memchr", "mio", - "mio-uds", + "num_cpus", + "parking_lot", + "pin-project-lite", "signal-hook-registry", - "tokio-executor", - "tokio-io", - "tokio-reactor", - "winapi 0.3.9", -] - -[[package]] -name = "tokio-sync" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee" -dependencies = [ - "fnv", - "futures", + "socket2", + "tokio-macros", + "windows-sys 0.45.0", ] [[package]] -name = "tokio-tcp" -version = "0.1.4" +name = "tokio-macros" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" dependencies = [ - "bytes", - "futures", - "iovec", - "mio", - "tokio-io", - "tokio-reactor", + "proc-macro2 1.0.53", + "quote 1.0.26", + "syn 1.0.109", ] [[package]] -name = "tokio-timer" -version = "0.2.13" +name = "tokio-util" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296" +checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" dependencies = [ - "crossbeam-utils 0.7.2", - "futures", - "slab", - "tokio-executor", + "bytes 1.4.0", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", ] [[package]] -name = "tokio-udp" -version = "0.1.6" +name = "tracing" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2a0b10e610b39c38b031a2fcab08e4b82f16ece36504988dcbd81dbba650d82" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ - "bytes", - "futures", + "cfg-if", "log", - "mio", - "tokio-codec", - "tokio-io", - "tokio-reactor", + "pin-project-lite", + "tracing-core", ] [[package]] -name = "trust-dns-proto" -version = "0.7.4" +name = "tracing-core" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5559ebdf6c2368ddd11e20b11d6bbaf9e46deb803acd7815e93f5a7b4a6d2901" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" dependencies = [ - "byteorder", - "enum-as-inner", - "failure", - "futures", - "idna 0.1.5", - "lazy_static", - "log", - "rand 0.6.5", - "smallvec 0.6.14", - "socket2", - "tokio-executor", - "tokio-io", - "tokio-reactor", - "tokio-tcp", - "tokio-timer", - "tokio-udp", - "url 1.7.2", + "once_cell", ] [[package]] -name = "trust-dns-resolver" -version = "0.11.1" +name = "typenum" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c9992e58dba365798803c0b91018ff6c8d3fc77e06977c4539af2a6bfe0a039" -dependencies = [ - "cfg-if 0.1.10", - "failure", - "futures", - "ipconfig", - "lazy_static", - "log", - "lru-cache", - "resolv-conf", - "smallvec 0.6.14", - "tokio-executor", - "trust-dns-proto", -] +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "unicode-bidi" -version = "0.3.8" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.2" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "unicode-normalization" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ "tinyvec", ] @@ -3081,12 +2629,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" -[[package]] -name = "unicode-xid" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" - [[package]] name = "url" version = "1.7.2" @@ -3100,14 +2642,13 @@ dependencies = [ [[package]] name = "url" -version = "2.2.2" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" dependencies = [ "form_urlencoded", - "idna 0.2.3", - "matches", - "percent-encoding 2.1.0", + "idna 0.3.0", + "percent-encoding 2.2.0", ] [[package]] @@ -3124,12 +2665,11 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "walkdir" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" dependencies = [ "same-file", - "winapi 0.3.9", "winapi-util", ] @@ -3153,80 +2693,68 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.81" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.81" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" dependencies = [ "bumpalo", - "lazy_static", "log", - "proc-macro2 1.0.41", - "quote 1.0.20", - "syn 1.0.98", + "once_cell", + "proc-macro2 1.0.53", + "quote 1.0.26", + "syn 1.0.109", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.81" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" dependencies = [ - "quote 1.0.20", + "quote 1.0.26", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.81" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ - "proc-macro2 1.0.41", - "quote 1.0.20", - "syn 1.0.98", + "proc-macro2 1.0.53", + "quote 1.0.26", + "syn 1.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.81" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" [[package]] name = "web-sys" -version = "0.3.58" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fed94beee57daf8dd7d51f2b15dc2bcde92d7a72304cdf662a4371008b71b90" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" dependencies = [ "js-sys", "wasm-bindgen", ] -[[package]] -name = "widestring" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c" - -[[package]] -name = "winapi" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" - [[package]] name = "winapi" version = "0.3.9" @@ -3237,12 +2765,6 @@ dependencies = [ "winapi-x86_64-pc-windows-gnu", ] -[[package]] -name = "winapi-build" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" - [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" @@ -3255,7 +2777,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ - "winapi 0.3.9", + "winapi", ] [[package]] @@ -3264,64 +2786,92 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + [[package]] name = "windows-sys" -version = "0.36.1" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ + "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", + "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_msvc" -version = "0.36.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_i686_gnu" -version = "0.36.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_msvc" -version = "0.36.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_x86_64_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.36.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] -name = "winreg" -version = "0.6.2" +name = "windows_x86_64_gnullvm" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9" -dependencies = [ - "winapi 0.3.9", -] +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] -name = "ws2_32-sys" -version = "0.2.1" +name = "windows_x86_64_msvc" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" -dependencies = [ - "winapi 0.2.8", - "winapi-build", -] +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" diff --git a/Cargo.toml b/Cargo.toml index ff0918b2..73f56706 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,23 @@ [workspace] -members = ["common", "lz4-compress", "jemallocator/jemalloc-sys", "preload", "cli-core", "cli", "server-core", "gather", "integration-tests", "mimalloc_rust", "fast_range_map"] +members = [ + "common", + "lz4-compress", + "jemallocator/jemalloc-sys", + "preload", + "cli-core", + "cli", + "server-core", + "gather", + "integration-tests", + "mimalloc_rust", + "fast_range_map", +] resolver = "2" [profile.dev] -opt-level = 2 +opt-level = 0 incremental = true +debug = true [profile.release] opt-level = 3 diff --git a/cli-core/Cargo.toml b/cli-core/Cargo.toml index 582753bd..6d59b1cd 100644 --- a/cli-core/Cargo.toml +++ b/cli-core/Cargo.toml @@ -8,24 +8,24 @@ edition = "2018" smallvec = "1" byteorder = "1" ctrlc = "3" -goblin = "0.0.24" +goblin = "0.6.1" string-interner = { version = "0.7", default-features = false } -cpp_demangle = "0.2" +cpp_demangle = "0.4" chrono = "0.4" libc = "0.2" log = "0.4" -lru = "0.6" -bitflags = "1" -inferno = { version = "0.9", default-features = false } +lru = "0.10" +bitflags = "2" +inferno = { version = "0.11", default-features = false } lazy_static = "1" -ahash = "0.7" +ahash = "0.8" parking_lot = "0.12" crossbeam-channel = "0.5" rayon = "1" regex = "1" rhai = { version = "1", features = ["unchecked"] } plotters = { version = "0.3", default-features = false, features = ["svg_backend", "all_series"] } -colorgrad = "0.4" +colorgrad = "0.6" serde_json = "1" derive_more = "0.99" diff --git a/cli-core/src/cmd_analyze_size.rs b/cli-core/src/cmd_analyze_size.rs index 6cab53dd..0e4ae05d 100644 --- a/cli-core/src/cmd_analyze_size.rs +++ b/cli-core/src/cmd_analyze_size.rs @@ -1,21 +1,21 @@ -use std::io::{self, Read}; +use crate::reader::parse_events; use ahash::AHashMap as HashMap; use common::event::Event; use common::Timestamp; -use crate::reader::parse_events; +use std::io::{self, Read}; -fn format_count( count: usize ) -> String { +fn format_count(count: usize) -> String { if count < 1000 { - format!( "{}", count ) + format!("{}", count) } else if count < 1000 * 1000 { - format!( "{}K", count / 1000 ) + format!("{}K", count / 1000) } else { - format!( "{}M", count / (1000 * 1000) ) + format!("{}M", count / (1000 * 1000)) } } -pub fn analyze_size( fp: impl Read + Send + 'static ) -> Result< (), io::Error > { - let (_, event_stream) = parse_events( fp )?; +pub fn analyze_size(fp: impl Read + Send + 'static) -> Result<(), io::Error> { + let (_, event_stream) = parse_events(fp)?; const S_OTHER: usize = 0; const S_ALLOC: usize = 1; @@ -43,16 +43,16 @@ pub fn analyze_size( fp: impl Read + Send + 'static ) -> Result< (), io::Error > #[derive(Default)] struct Stats { size: usize, - count: usize + count: usize, } let mut stats = Vec::new(); - stats.resize_with( S_MAX, Stats::default ); + stats.resize_with(S_MAX, Stats::default); let mut allocation_buckets = Vec::new(); - allocation_buckets.resize( 10, 0 ); + allocation_buckets.resize(10, 0); - fn elapsed_to_bucket( elapsed: Timestamp ) -> usize { + fn elapsed_to_bucket(elapsed: Timestamp) -> usize { let s = elapsed.as_secs(); if s < 1 { 0 @@ -78,56 +78,61 @@ pub fn analyze_size( fp: impl Read + Send + 'static ) -> Result< (), io::Error > let mut allocations = HashMap::new(); for event in event_stream { let event = match event { - Ok( event ) => event, - Err( _ ) => break + Ok(event) => event, + Err(_) => break, }; - let size = common::speedy::Writable::< common::speedy::LittleEndian >::bytes_needed( &event ).unwrap(); + let size = + common::speedy::Writable::::bytes_needed(&event).unwrap(); let kind = match event { - | Event::Alloc { .. } => S_ALLOC, - | Event::AllocEx { id, timestamp, .. } => { - allocations.insert( id, timestamp ); + Event::Alloc { .. } => S_ALLOC, + Event::AllocEx { id, timestamp, .. } => { + allocations.insert(id, timestamp); S_ALLOC - }, - | Event::Realloc { .. } - | Event::ReallocEx { .. } => S_REALLOC, - | Event::Free { .. } => S_FREE, - | Event::FreeEx { id, timestamp, .. } => { - if let Some( allocated_timestamp ) = allocations.remove( &id ) { + } + Event::Realloc { .. } | Event::ReallocEx { .. } => S_REALLOC, + Event::Free { .. } => S_FREE, + Event::FreeEx { id, timestamp, .. } => { + if let Some(allocated_timestamp) = allocations.remove(&id) { let elapsed = timestamp - allocated_timestamp; - allocation_buckets[ elapsed_to_bucket( elapsed ) ] += 1; + allocation_buckets[elapsed_to_bucket(elapsed)] += 1; } S_FREE - }, - | Event::Backtrace { .. } + } + Event::Backtrace { .. } | Event::PartialBacktrace { .. } | Event::PartialBacktrace32 { .. } | Event::Backtrace32 { .. } => S_BACKTRACE, - | Event::File { .. } => S_FILE, - | Event::File64 { .. } => S_FILE, - | Event::GroupStatistics { .. } => S_STATS, - | Event::AddRegion { .. } - | Event::RemoveRegion { .. } - | Event::MemoryMapEx { .. } => S_MAPS, - | Event::UpdateRegionUsage { .. } => S_MAPS_USAGE, - _ => S_OTHER + Event::File { .. } => S_FILE, + Event::File64 { .. } => S_FILE, + Event::GroupStatistics { .. } => S_STATS, + Event::AddRegion { .. } | Event::RemoveRegion { .. } | Event::MemoryMapEx { .. } => { + S_MAPS + } + Event::UpdateRegionUsage { .. } => S_MAPS_USAGE, + _ => S_OTHER, }; - stats[ kind ].size += size; - stats[ kind ].count += 1; + stats[kind].size += size; + stats[kind].count += 1; } *allocation_buckets.last_mut().unwrap() += allocations.len(); - let mut stats: Vec< _ > = stats.into_iter().enumerate().collect(); - stats.sort_by_key( |(_, stats)| !stats.size ); + let mut stats: Vec<_> = stats.into_iter().enumerate().collect(); + stats.sort_by_key(|(_, stats)| !stats.size); - println!( "Total event sizes:" ); + println!("Total event sizes:"); for (index, stats) in stats { - println!( " {}: {}MB ({} events)", SIZE_TO_NAME[ index ], stats.size / (1024 * 1024), format_count( stats.count ) ); + println!( + " {}: {}MB ({} events)", + SIZE_TO_NAME[index], + stats.size / (1024 * 1024), + format_count(stats.count) + ); } - println!( "\nAllocation lifetime buckets:" ); + println!("\nAllocation lifetime buckets:"); for (index, count) in allocation_buckets.into_iter().enumerate() { let label = match index { 0 => "< 1s", @@ -140,11 +145,11 @@ pub fn analyze_size( fp: impl Read + Send + 'static ) -> Result< (), io::Error > 7 => "< 1h", 8 => ">= 1h", 9 => "Leaked", - _ => unreachable!() + _ => unreachable!(), }; - println!( " {}: {}", label, format_count( count ) ); + println!(" {}: {}", label, format_count(count)); } Ok(()) -} \ No newline at end of file +} diff --git a/cli-core/src/cmd_extract.rs b/cli-core/src/cmd_extract.rs index d9f5c75f..203b4f43 100644 --- a/cli-core/src/cmd_extract.rs +++ b/cli-core/src/cmd_extract.rs @@ -1,62 +1,62 @@ -use std::path::PathBuf; -use std::fs::File; -use std::collections::HashMap; -use std::collections::hash_map::Entry; -use common::event::Event; use crate::reader::parse_events; +use common::event::Event; +use std::collections::hash_map::Entry; +use std::collections::HashMap; +use std::fs::File; +use std::path::PathBuf; -pub fn extract( input: PathBuf, output: PathBuf ) -> Result< (), std::io::Error > { - info!( "Opening {:?}...", input ); - let fp = File::open( input )?; - let (_, event_stream) = parse_events( fp )?; +pub fn extract(input: PathBuf, output: PathBuf) -> Result<(), std::io::Error> { + info!("Opening {:?}...", input); + let fp = File::open(input)?; + let (_, event_stream) = parse_events(fp)?; - info!( "Creating {:?} if it doesn't exist...", output ); - std::fs::create_dir_all( &output )?; + info!("Creating {:?} if it doesn't exist...", output); + std::fs::create_dir_all(&output)?; let mut counter = HashMap::new(); for event in event_stream { let event = match event { - Ok( event ) => event, - Err( _ ) => break + Ok(event) => event, + Err(_) => break, }; match event { Event::File { path, contents, .. } | Event::File64 { path, contents, .. } => { let mut relative_path = &*path; - if relative_path.starts_with( "/" ) { - relative_path = &relative_path[ 1.. ]; + if relative_path.starts_with("/") { + relative_path = &relative_path[1..]; } - let mut target_path = output.join( relative_path ); + let mut target_path = output.join(relative_path); - info!( "Extracting {:?} into {:?}...", path, target_path ); - if let Some( parent ) = target_path.parent() { - std::fs::create_dir_all( parent )?; + info!("Extracting {:?} into {:?}...", path, target_path); + if let Some(parent) = target_path.parent() { + std::fs::create_dir_all(parent)?; } - match counter.entry( target_path.clone() ) { - Entry::Vacant( bucket ) => { - bucket.insert( 0 ); - }, - Entry::Occupied( mut bucket ) => { + match counter.entry(target_path.clone()) { + Entry::Vacant(bucket) => { + bucket.insert(0); + } + Entry::Occupied(mut bucket) => { let parent = target_path.parent().unwrap(); let mut filename = target_path.file_name().unwrap().to_os_string(); if *bucket.get() == 0 { let mut filename = filename.clone(); - filename.push( ".000" ); - std::fs::rename( &target_path, parent.join( filename ) )?; + filename.push(".000"); + std::fs::rename(&target_path, parent.join(filename))?; } - filename.push( format!( ".{:03}", bucket.get() ) ); - target_path = parent.join( filename ); + filename.push(format!(".{:03}", bucket.get())); + target_path = parent.join(filename); *bucket.get_mut() += 1; } } - std::fs::write( target_path, contents )?; - }, + std::fs::write(target_path, contents)?; + } _ => {} } } diff --git a/cli-core/src/cmd_gather.rs b/cli-core/src/cmd_gather.rs index 3268caaa..14ee1fec 100644 --- a/cli-core/src/cmd_gather.rs +++ b/cli-core/src/cmd_gather.rs @@ -1,91 +1,105 @@ +use std::collections::{HashMap, HashSet}; use std::error::Error; -use std::net::{UdpSocket, TcpStream, ToSocketAddrs, IpAddr, SocketAddr, Ipv4Addr}; use std::fs::File; -use std::io::{self, Write, ErrorKind}; -use std::thread; -use std::collections::{HashMap, HashSet}; +use std::io::{self, ErrorKind, Write}; +use std::mem; +use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpStream, ToSocketAddrs, UdpSocket}; use std::sync::{Arc, Mutex, MutexGuard}; +use std::thread; use std::time::{Duration, Instant}; -use std::mem; use chrono::prelude::*; use common::speedy::{Readable, Writable}; -use common::request::{PROTOCOL_VERSION, BroadcastHeader, Request, Response}; -use common::get_local_ips; use common::event::DataId; +use common::get_local_ips; +use common::request::{BroadcastHeader, Request, Response, PROTOCOL_VERSION}; -use crate::util::{ReadableDuration, Sigint, on_ctrlc}; +use crate::util::{on_ctrlc, ReadableDuration, Sigint}; struct Wrapper { sigint: Sigint, - stream: TcpStream + stream: TcpStream, } impl Wrapper { - fn new( sigint: Sigint, stream: TcpStream ) -> io::Result< Self > { - stream.set_read_timeout( Some( Duration::from_secs( 5 ) ) )?; - Ok( Wrapper { - sigint, - stream - }) + fn new(sigint: Sigint, stream: TcpStream) -> io::Result { + stream.set_read_timeout(Some(Duration::from_secs(5)))?; + Ok(Wrapper { sigint, stream }) } } impl io::Read for Wrapper { - fn read( &mut self, buffer: &mut [u8] ) -> io::Result< usize > { + fn read(&mut self, buffer: &mut [u8]) -> io::Result { loop { if self.sigint.was_sent() { - return Err( io::Error::new( io::ErrorKind::Other, "interrupted by SIGINT" ) ); + return Err(io::Error::new( + io::ErrorKind::Other, + "interrupted by SIGINT", + )); } - match self.stream.read( buffer ) { - Err( ref error ) if error.kind() == ErrorKind::WouldBlock || error.kind() == ErrorKind::TimedOut => { - Request::Ping.write_to_stream( &self.stream )?; + match self.stream.read(buffer) { + Err(ref error) + if error.kind() == ErrorKind::WouldBlock + || error.kind() == ErrorKind::TimedOut => + { + Request::Ping.write_to_stream(&self.stream)?; continue; - }, - result => return result + } + result => return result, } } } } -fn client_loop( socket: TcpStream, mut fp: File, sigint: Sigint, mut ip_lock: Option< MutexGuard< () > > ) -> Result< (), io::Error > { +fn client_loop( + socket: TcpStream, + mut fp: File, + sigint: Sigint, + mut ip_lock: Option>, +) -> Result<(), io::Error> { let timestamp = Instant::now(); let address = socket.peer_addr().unwrap(); - let mut socket = Wrapper::new( sigint.clone(), socket )?; + let mut socket = Wrapper::new(sigint.clone(), socket)?; while !sigint.was_sent() { - if ip_lock.is_some() && timestamp.elapsed() > Duration::from_secs( 60 ) { + if ip_lock.is_some() && timestamp.elapsed() > Duration::from_secs(60) { ip_lock = None; } - let response = Response::read_from_stream_unbuffered( &mut socket ); + let response = Response::read_from_stream_unbuffered(&mut socket); match response { - Ok( Response::Data( data ) ) => { - fp.write_all( &data )?; - }, - Ok( Response::FinishedInitialStreaming ) => { - info!( "Initial data received from {}; starting online gathering...", address ); + Ok(Response::Data(data)) => { + fp.write_all(&data)?; + } + Ok(Response::FinishedInitialStreaming) => { + info!( + "Initial data received from {}; starting online gathering...", + address + ); // This is here to avoid triggering an avalanche // of simultaneous downloads when we're gathering from // multiple sources which originate from the same machine. let ip_lock = ip_lock.take(); - mem::drop( ip_lock ); - }, - Ok( Response::Finished ) => { - info!( "Received an explicit finish from {}", address ); + mem::drop(ip_lock); + } + Ok(Response::Finished) => { + info!("Received an explicit finish from {}", address); return Ok(()); - }, - Ok( _ ) => {}, - Err( error ) => { + } + Ok(_) => {} + Err(error) => { let error: io::Error = error.into(); - if error.kind() == ErrorKind::UnexpectedEof || (error.kind() == ErrorKind::Other && format!( "{}", error ) == "interrupted by SIGINT") { + if error.kind() == ErrorKind::UnexpectedEof + || (error.kind() == ErrorKind::Other + && format!("{}", error) == "interrupted by SIGINT") + { return Ok(()); } - return Err( error ); + return Err(error); } } } @@ -93,97 +107,128 @@ fn client_loop( socket: TcpStream, mut fp: File, sigint: Sigint, mut ip_lock: Op Ok(()) } -fn connect< A: ToSocketAddrs >( target: A ) -> Result< (TcpStream, File, String), io::Error > { - let socket = TcpStream::connect( target )?; +fn connect(target: A) -> Result<(TcpStream, File, String), io::Error> { + let socket = TcpStream::connect(target)?; let target = socket.peer_addr().unwrap(); - let response = Response::read_from_stream_unbuffered( &socket )?; + let response = Response::read_from_stream_unbuffered(&socket)?; match response { - Response::Start( BroadcastHeader { pid, executable, arch, timestamp, initial_timestamp, .. } ) => { - let executable = String::from_utf8_lossy( &executable ); - info!( "Connection established to {}:", target ); - info!( " Executable: {}", executable ); - info!( " Uptime: {}", ReadableDuration( timestamp.as_secs() - initial_timestamp.as_secs() ) ); - info!( " PID: {}", pid ); - info!( " Arch: {}", arch ); - - let basename: String = executable[ executable.rfind( "/" ).map( |index| index + 1 ).unwrap_or( 0 ).. ].chars().map( |ch| { - if ch.is_alphanumeric() { - ch - } else { - '_' - } - }).collect(); + Response::Start(BroadcastHeader { + pid, + executable, + arch, + timestamp, + initial_timestamp, + .. + }) => { + let executable = String::from_utf8_lossy(&executable); + info!("Connection established to {}:", target); + info!(" Executable: {}", executable); + info!( + " Uptime: {}", + ReadableDuration(timestamp.as_secs() - initial_timestamp.as_secs()) + ); + info!(" PID: {}", pid); + info!(" Arch: {}", arch); + + let basename: String = executable + [executable.rfind("/").map(|index| index + 1).unwrap_or(0)..] + .chars() + .map(|ch| if ch.is_alphanumeric() { ch } else { '_' }) + .collect(); let now = Utc::now(); - let filename = format!( "{}{:02}{:02}_{:02}{:02}{:02}_{:05}_{}.dat", now.year(), now.month(), now.day(), now.hour(), now.minute(), now.second(), pid, basename ); - info!( "Gathering events to '{}'...", filename ); - - let fp = match File::create( &filename ) { - Ok( fp ) => fp, - Err( error ) => { - error!( "Unable to create '{}': {}", filename, error ); - return Err( io::Error::new( io::ErrorKind::Other, "unable to create output file" ) ); + let filename = format!( + "{}{:02}{:02}_{:02}{:02}{:02}_{:05}_{}.dat", + now.year(), + now.month(), + now.day(), + now.hour(), + now.minute(), + now.second(), + pid, + basename + ); + info!("Gathering events to '{}'...", filename); + + let fp = match File::create(&filename) { + Ok(fp) => fp, + Err(error) => { + error!("Unable to create '{}': {}", filename, error); + return Err(io::Error::new( + io::ErrorKind::Other, + "unable to create output file", + )); } }; - Request::StartStreaming.write_to_stream( &socket )?; + Request::StartStreaming.write_to_stream(&socket)?; - Ok( (socket, fp, filename) ) - }, - _ => return Err( io::Error::new( io::ErrorKind::Other, "unexpected message" ) ) + Ok((socket, fp, filename)) + } + _ => return Err(io::Error::new(io::ErrorKind::Other, "unexpected message")), } } struct ClientLifetime { id: DataId, - clients: Arc< Mutex< HashSet< DataId > > > + clients: Arc>>, } impl ClientLifetime { - fn new( clients: &Arc< Mutex< HashSet< DataId > > >, id: DataId ) -> Option< ClientLifetime > { + fn new(clients: &Arc>>, id: DataId) -> Option { let mut guard = clients.lock().unwrap(); - if guard.contains( &id ) { + if guard.contains(&id) { return None; } - guard.insert( id ); - Some( ClientLifetime { + guard.insert(id); + Some(ClientLifetime { id, - clients: clients.clone() + clients: clients.clone(), }) } } impl Drop for ClientLifetime { - fn drop( &mut self ) { - self.clients.lock().unwrap().remove( &self.id ); + fn drop(&mut self) { + self.clients.lock().unwrap().remove(&self.id); } } -pub fn main( target: Option< &str > ) -> Result< (), Box< dyn Error > > { - let clients: Arc< Mutex< HashSet< DataId > > > = Arc::new( Mutex::new( HashSet::new() ) ); - let mut locks: HashMap< IpAddr, Arc< Mutex< () > > > = HashMap::new(); +pub fn main(target: Option<&str>) -> Result<(), Box> { + let clients: Arc>> = Arc::new(Mutex::new(HashSet::new())); + let mut locks: HashMap>> = HashMap::new(); let sigint = on_ctrlc(); match target { None => { let mut buffer = Vec::new(); - buffer.resize( 1024 * 8, 0 ); - let socket = UdpSocket::bind( "0.0.0.0:43512" ).expect( "cannot bind the UDP socket" ); - socket.set_read_timeout( Some( Duration::from_millis( 100 ) ) ).expect( "cannot set read timeout" ); + buffer.resize(1024 * 8, 0); + let socket = UdpSocket::bind("0.0.0.0:43512").expect("cannot bind the UDP socket"); + socket + .set_read_timeout(Some(Duration::from_millis(100))) + .expect("cannot set read timeout"); - info!( "Scanning..." ); + info!("Scanning..."); while !sigint.was_sent() { - if let Ok( (byte_count, addr) ) = socket.recv_from( &mut buffer ) { - let ip = if get_local_ips().iter().any( |&local_ip| addr.ip() == local_ip ) { - IpAddr::V4( Ipv4Addr::new( 127, 0, 0, 1 ) ) + if let Ok((byte_count, addr)) = socket.recv_from(&mut buffer) { + let ip = if get_local_ips() + .iter() + .any(|&local_ip| addr.ip() == local_ip) + { + IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)) } else { addr.ip() }; - let start_body = match BroadcastHeader::read_from_buffer( &buffer[ ..byte_count ] ) { - Ok( start_body ) => start_body, - Err( err ) => { - error!( "Failed to deserialize broadcast handshake packet from '{}': {:?}", addr.ip(), err ); + let start_body = match BroadcastHeader::read_from_buffer(&buffer[..byte_count]) + { + Ok(start_body) => start_body, + Err(err) => { + error!( + "Failed to deserialize broadcast handshake packet from '{}': {:?}", + addr.ip(), + err + ); continue; } }; @@ -200,45 +245,51 @@ pub fn main( target: Option< &str > ) -> Result< (), Box< dyn Error > > { } let id = start_body.id; - let lifetime = match ClientLifetime::new( &clients, id ) { - Some( lifetime ) => lifetime, - None => continue + let lifetime = match ClientLifetime::new(&clients, id) { + Some(lifetime) => lifetime, + None => continue, }; - let addr = SocketAddr::new( ip, start_body.listener_port ); - info!( "Found a new client {}", addr ); + let addr = SocketAddr::new(ip, start_body.listener_port); + info!("Found a new client {}", addr); let sigint = sigint.clone(); - let ip_lock = locks.entry( addr.ip() ).or_insert_with( || Arc::new( Mutex::new(()) ) ).clone(); - thread::spawn( move || { + let ip_lock = locks + .entry(addr.ip()) + .or_insert_with(|| Arc::new(Mutex::new(()))) + .clone(); + thread::spawn(move || { let _lifetime = lifetime; let ip_lock = ip_lock.lock().unwrap(); - info!( "Trying to connect to {}...", addr ); - let (socket, fp, filename) = match connect( addr ) { - Ok( value ) => value, - Err( err ) => { - error!( "Failed to connect to '{}': {}", addr, err ); + info!("Trying to connect to {}...", addr); + let (socket, fp, filename) = match connect(addr) { + Ok(value) => value, + Err(err) => { + error!("Failed to connect to '{}': {}", addr, err); return; } }; - match client_loop( socket, fp, sigint, Some( ip_lock ) ) { - Ok(()) => info!( "Gathering finished for {}; '{}' is now complete", addr, filename ), - Err( err ) => error!( "Gathering failed for {}: {:?}", addr, err ) + match client_loop(socket, fp, sigint, Some(ip_lock)) { + Ok(()) => info!( + "Gathering finished for {}; '{}' is now complete", + addr, filename + ), + Err(err) => error!("Gathering failed for {}: {:?}", addr, err), } }); } } - }, - Some( target ) => { - let (socket, fp, _) = connect( target )?; - match client_loop( socket, fp, sigint, None ) { - Ok(()) => info!( "Gathering finished successfully!" ), - Err( err ) => error!( "Gathering failed: {:?}", err ) + } + Some(target) => { + let (socket, fp, _) = connect(target)?; + match client_loop(socket, fp, sigint, None) { + Ok(()) => info!("Gathering finished successfully!"), + Err(err) => error!("Gathering failed: {:?}", err), } } } - info!( "Finished!" ); + info!("Finished!"); Ok(()) } diff --git a/cli-core/src/data.rs b/cli-core/src/data.rs index 5d19a2d8..3d08117b 100644 --- a/cli-core/src/data.rs +++ b/cli-core/src/data.rs @@ -1,124 +1,120 @@ -use std::fmt; -use std::ops::Range; -use std::num::{NonZeroU32, NonZeroU64}; -use std::cmp::Ordering; use std::borrow::{Borrow, Cow}; -use std::iter::FusedIterator; +use std::cmp::Ordering; use std::collections::BTreeMap; +use std::fmt; +use std::iter::FusedIterator; +use std::num::{NonZeroU32, NonZeroU64}; +use std::ops::Range; use ahash::AHashMap as HashMap; use string_interner; +use crate::frame::Frame; use crate::tree::Tree; use crate::tree_printer::dump_tree; -use crate::frame::Frame; +use crate::util::{table_to_string, ReadableSize}; use crate::vecvec::DenseVecVec; -use crate::util::{ReadableSize, table_to_string}; -pub use common::{Timestamp}; pub use common::event::DataId; pub use common::event::RegionFlags; +pub use common::Timestamp; -pub type StringInterner = string_interner::StringInterner< StringId >; +pub type StringInterner = string_interner::StringInterner; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] #[repr(transparent)] -pub struct StringId( NonZeroU32 ); +pub struct StringId(NonZeroU32); impl string_interner::Symbol for StringId { #[inline] - fn from_usize( value: usize ) -> Self { - unsafe { - StringId( NonZeroU32::new_unchecked( (value + 1) as u32 ) ) - } + fn from_usize(value: usize) -> Self { + unsafe { StringId(NonZeroU32::new_unchecked((value + 1) as u32)) } } #[inline] - fn to_usize( self ) -> usize { + fn to_usize(self) -> usize { self.0.get() as usize - 1 } } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] #[repr(transparent)] -pub struct AllocationId( NonZeroU64 ); +pub struct AllocationId(NonZeroU64); impl AllocationId { #[inline(always)] - pub(crate) fn new( raw: u64 ) -> Self { - unsafe { - AllocationId( NonZeroU64::new_unchecked( raw + 1 ) ) - } + pub(crate) fn new(raw: u64) -> Self { + unsafe { AllocationId(NonZeroU64::new_unchecked(raw + 1)) } } #[inline(always)] - pub fn raw( &self ) -> u64 { + pub fn raw(&self) -> u64 { self.0.get() - 1 } } #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[repr(transparent)] -pub struct OperationId( u64 ); +pub struct OperationId(u64); impl OperationId { #[inline] - pub fn new_allocation( id: AllocationId ) -> Self { - OperationId( id.raw() ) + pub fn new_allocation(id: AllocationId) -> Self { + OperationId(id.raw()) } #[inline] - pub fn new_deallocation( id: AllocationId ) -> Self { - OperationId( (1 << 62) | id.raw() ) + pub fn new_deallocation(id: AllocationId) -> Self { + OperationId((1 << 62) | id.raw()) } #[inline] - pub fn new_reallocation( id: AllocationId ) -> Self { - OperationId( (2 << 62) | id.raw() ) + pub fn new_reallocation(id: AllocationId) -> Self { + OperationId((2 << 62) | id.raw()) } #[inline] - pub fn is_allocation( &self ) -> bool { + pub fn is_allocation(&self) -> bool { (self.0 >> 62) == 0 } #[allow(dead_code)] #[inline] - pub fn is_deallocation( &self ) -> bool { + pub fn is_deallocation(&self) -> bool { (self.0 >> 62) == 1 } #[inline] - pub fn is_reallocation( &self ) -> bool { + pub fn is_reallocation(&self) -> bool { (self.0 >> 62) == 2 } #[inline] - pub fn id( &self ) -> AllocationId { - AllocationId::new( self.0 & !(3 << 62) ) + pub fn id(&self) -> AllocationId { + AllocationId::new(self.0 & !(3 << 62)) } } #[test] fn test_operation_id() { - let max = AllocationId::new( 0x3fff_ffff_ffff_ffff ); - let id = OperationId::new_allocation( max ); - assert!( id.is_allocation() ); - assert!( !id.is_reallocation() ); - assert!( !id.is_deallocation() ); - assert_eq!( id.id(), max ); - - let id = OperationId::new_deallocation( max ); - assert!( !id.is_allocation() ); - assert!( !id.is_reallocation() ); - assert!( id.is_deallocation() ); - assert_eq!( id.id(), max ); - - let id = OperationId::new_reallocation( max ); - assert!( !id.is_allocation() ); - assert!( id.is_reallocation() ); - assert!( !id.is_deallocation() ); - assert_eq!( id.id(), max ); + let max = AllocationId::new(0x3fff_ffff_ffff_ffff); + let id = OperationId::new_allocation(max); + assert!(id.is_allocation()); + assert!(!id.is_reallocation()); + assert!(!id.is_deallocation()); + assert_eq!(id.id(), max); + + let id = OperationId::new_deallocation(max); + assert!(!id.is_allocation()); + assert!(!id.is_reallocation()); + assert!(id.is_deallocation()); + assert_eq!(id.id(), max); + + let id = OperationId::new_reallocation(max); + assert!(!id.is_allocation()); + assert!(id.is_reallocation()); + assert!(!id.is_deallocation()); + assert_eq!(id.id(), max); } pub struct Data { @@ -130,25 +126,25 @@ pub struct Data { pub(crate) architecture: String, pub(crate) pointer_size: u64, pub(crate) interner: StringInterner, - pub(crate) operations: Vec< OperationId >, - pub(crate) allocations: Vec< Allocation >, - pub(crate) sorted_by_timestamp: Vec< AllocationId >, - pub(crate) sorted_by_address: Vec< AllocationId >, - pub(crate) sorted_by_size: Vec< AllocationId >, - pub(crate) frames: Vec< Frame >, - pub(crate) backtraces: Vec< BacktraceStorageRef >, - pub(crate) backtraces_storage: Vec< FrameId >, - pub(crate) allocations_by_backtrace: DenseVecVec< AllocationId >, + pub(crate) operations: Vec, + pub(crate) allocations: Vec, + pub(crate) sorted_by_timestamp: Vec, + pub(crate) sorted_by_address: Vec, + pub(crate) sorted_by_size: Vec, + pub(crate) frames: Vec, + pub(crate) backtraces: Vec, + pub(crate) backtraces_storage: Vec, + pub(crate) allocations_by_backtrace: DenseVecVec, pub(crate) total_allocated: u64, pub(crate) total_allocated_count: u64, pub(crate) total_freed: u64, pub(crate) total_freed_count: u64, - pub(crate) mallopts: Vec< Mallopt >, + pub(crate) mallopts: Vec, pub(crate) maximum_backtrace_depth: u32, - pub(crate) group_stats: Vec< GroupStatistics >, - pub(crate) chains: HashMap< AllocationId, AllocationChain >, - pub(crate) maps: Vec< Map >, - pub(crate) map_ids: Vec< MapId >, + pub(crate) group_stats: Vec, + pub(crate) chains: HashMap, + pub(crate) maps: Vec, + pub(crate) map_ids: Vec, } pub type DataPointer = u64; @@ -157,50 +153,50 @@ pub type ThreadId = u32; #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] #[repr(transparent)] -pub struct CodePointer( u64 ); +pub struct CodePointer(u64); impl CodePointer { - pub fn new( address: u64 ) -> Self { - CodePointer( address ) + pub fn new(address: u64) -> Self { + CodePointer(address) } - pub fn raw( &self ) -> u64 { + pub fn raw(&self) -> u64 { self.0 } } impl fmt::Display for CodePointer { - fn fmt( &self, formatter: &mut fmt::Formatter ) -> fmt::Result { - write!( formatter, "{:016X}", self.0 ) + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "{:016X}", self.0) } } -impl From< CodePointer > for u64 { +impl From for u64 { #[inline] - fn from( value: CodePointer ) -> Self { + fn from(value: CodePointer) -> Self { value.0 } } -impl From< u64 > for CodePointer { +impl From for CodePointer { #[inline] - fn from( value: u64 ) -> Self { - CodePointer( value ) + fn from(value: u64) -> Self { + CodePointer(value) } } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] #[repr(transparent)] -pub struct BacktraceId( u32 ); +pub struct BacktraceId(u32); impl BacktraceId { #[inline] - pub fn new( raw_id: u32 ) -> Self { - BacktraceId( raw_id ) + pub fn new(raw_id: u32) -> Self { + BacktraceId(raw_id) } #[inline] - pub fn raw( &self ) -> u32 { + pub fn raw(&self) -> u32 { self.0 } } @@ -209,12 +205,13 @@ pub type BacktraceStorageRef = (u32, u32); #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum SourceKey { - Address( CodePointer ), - Location( StringId, u32 ), - Function( StringId ) + Address(CodePointer), + Location(StringId, u32), + Function(StringId), } bitflags! { + #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] pub struct AllocationFlags: u8 { const IS_PREV_IN_USE = 1 << 0; const IS_MMAPED = 1 << 1; @@ -229,7 +226,7 @@ bitflags! { pub struct AllocationChain { pub first: AllocationId, pub last: AllocationId, - pub length: u32 + pub length: u32, } #[derive(Debug)] @@ -239,10 +236,10 @@ pub struct Allocation { pub thread: ThreadId, pub size: u64, pub backtrace: BacktraceId, - pub deallocation: Option< Deallocation >, - pub reallocation: Option< AllocationId >, - pub reallocated_from: Option< AllocationId >, - pub first_allocation_in_chain: Option< AllocationId >, + pub deallocation: Option, + pub reallocation: Option, + pub reallocated_from: Option, + pub first_allocation_in_chain: Option, pub position_in_chain: u32, pub flags: AllocationFlags, pub extra_usable_space: u32, @@ -259,7 +256,7 @@ pub struct GroupStatistics { pub free_size: u64, pub min_size: u64, pub max_size: u64, - pub max_total_usage_first_seen_at: Timestamp + pub max_total_usage_first_seen_at: Timestamp, } impl Default for GroupStatistics { @@ -273,7 +270,7 @@ impl Default for GroupStatistics { free_size: 0, min_size: -1_i64 as u64, max_size: 0, - max_total_usage_first_seen_at: Timestamp::min() + max_total_usage_first_seen_at: Timestamp::min(), } } } @@ -283,30 +280,41 @@ pub struct MapSource { // This is the time when `mmap`/`munmap` was accually called. pub timestamp: Timestamp, pub backtrace: BacktraceId, - pub thread: ThreadId + pub thread: ThreadId, } #[derive(Clone, Debug)] pub struct MapRegionDeallocationSource { pub address: u64, pub length: u64, - pub source: MapSource + pub source: MapSource, } #[derive(Clone, Debug)] pub struct MapRegionDeallocation { // This is the time when smaps were read. pub timestamp: Timestamp, - pub sources: smallvec::SmallVec< [MapRegionDeallocationSource; 1] >, + pub sources: smallvec::SmallVec<[MapRegionDeallocationSource; 1]>, } #[derive(Clone, Debug)] pub struct MapDeallocation { pub timestamp: Timestamp, - pub source: Option< MapSource > + pub source: Option, } -#[derive(Copy, Clone, PartialEq, Eq, derive_more::Add, derive_more::Sub, derive_more::Neg, derive_more::AddAssign, Default, Debug)] +#[derive( + Copy, + Clone, + PartialEq, + Eq, + derive_more::Add, + derive_more::Sub, + derive_more::Neg, + derive_more::AddAssign, + Default, + Debug, +)] pub struct UsageDelta { pub address_space: i64, pub anonymous: i64, @@ -318,15 +326,15 @@ pub struct UsageDelta { } impl UsageDelta { - pub fn rss( &self ) -> i64 { + pub fn rss(&self) -> i64 { self.shared_clean + self.shared_dirty + self.private_clean + self.private_dirty } - pub fn clean( &self ) -> i64 { + pub fn clean(&self) -> i64 { self.shared_clean + self.private_clean } - pub fn dirty( &self ) -> i64 { + pub fn dirty(&self) -> i64 { self.shared_dirty + self.private_dirty } } @@ -345,7 +353,7 @@ pub struct MapUsage { } impl MapUsage { - pub fn rss( &self ) -> u64 { + pub fn rss(&self) -> u64 { self.shared_clean + self.shared_dirty + self.private_clean + self.private_dirty } } @@ -361,50 +369,70 @@ pub struct MapRegion { pub inode: u64, pub major: u32, pub minor: u32, - pub name: Box< str >, - pub deallocation: Option< MapRegionDeallocation >, + pub name: Box, + pub deallocation: Option, } #[derive(Debug)] pub struct Map { pub id: MapId, pub timestamp: Timestamp, - pub source: Option< MapSource >, - pub regions: smallvec::SmallVec< [MapRegion; 1] >, + pub source: Option, + pub regions: smallvec::SmallVec<[MapRegion; 1]>, // This is only `Some` when the whole map was deallocated. // It contains the very last deallocation. - pub deallocation: Option< MapDeallocation >, - pub usage_history: Vec< MapUsage >, + pub deallocation: Option, + pub usage_history: Vec, pub pointer: DataPointer, pub size: u64, pub flags: RegionFlags, - pub name: Box< str >, + pub name: Box, pub peak_rss: u64, } impl Map { - pub fn is_from_bytehound( &self ) -> bool { + pub fn is_from_bytehound(&self) -> bool { &*self.name == "[anon:bytehound]" } #[inline] - pub fn try_match_allocation( &self, allocation: &Allocation ) -> bool { - let map_timestamp = self.source.as_ref().map( |source| source.timestamp ).unwrap_or( self.timestamp ); - let map_timestamp_end = self.deallocation.as_ref().map( |deallocation| deallocation.source.as_ref().map( |source| source.timestamp ).unwrap_or( deallocation.timestamp ) ); + pub fn try_match_allocation(&self, allocation: &Allocation) -> bool { + let map_timestamp = self + .source + .as_ref() + .map(|source| source.timestamp) + .unwrap_or(self.timestamp); + let map_timestamp_end = self.deallocation.as_ref().map(|deallocation| { + deallocation + .source + .as_ref() + .map(|source| source.timestamp) + .unwrap_or(deallocation.timestamp) + }); - if !crate::util::overlaps( allocation.pointer..allocation.pointer + allocation.size, self.pointer..self.pointer + self.size ) { + if !crate::util::overlaps( + allocation.pointer..allocation.pointer + allocation.size, + self.pointer..self.pointer + self.size, + ) { return false; } - if allocation.timestamp < map_timestamp || map_timestamp_end.map( |map_timestamp_end| allocation.timestamp >= map_timestamp_end ).unwrap_or( false ) { + if allocation.timestamp < map_timestamp + || map_timestamp_end + .map(|map_timestamp_end| allocation.timestamp >= map_timestamp_end) + .unwrap_or(false) + { return false; } if self.regions.len() > 1 { for region in &self.regions { - if let Some( deallocation ) = region.deallocation.as_ref() { + if let Some(deallocation) = region.deallocation.as_ref() { for source in &deallocation.sources { - if source.address <= allocation.pointer && source.address + source.length >= allocation.pointer + allocation.size { + if source.address <= allocation.pointer + && source.address + source.length + >= allocation.pointer + allocation.size + { if allocation.timestamp >= source.source.timestamp { return false; } @@ -413,7 +441,6 @@ impl Map { } } } - } } @@ -422,10 +449,10 @@ impl Map { } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -pub struct MapId( pub u64 ); +pub struct MapId(pub u64); impl MapId { - pub fn raw( self ) -> u64 { + pub fn raw(self) -> u64 { self.0 } } @@ -494,49 +521,50 @@ pub struct Mallopt { pub thread: ThreadId, pub kind: MalloptKind, pub value: i32, - pub result: i32 + pub result: i32, } impl Allocation { #[inline] - pub fn was_deallocated( &self ) -> bool { + pub fn was_deallocated(&self) -> bool { self.deallocation.is_some() } #[inline] - pub fn is_shared_ptr( &self ) -> bool { - self.flags.contains( AllocationFlags::IS_SHARED_PTR ) + pub fn is_shared_ptr(&self) -> bool { + self.flags.contains(AllocationFlags::IS_SHARED_PTR) } #[inline] - pub fn in_non_main_arena( &self ) -> bool { - self.flags.contains( AllocationFlags::IN_NON_MAIN_ARENA ) + pub fn in_non_main_arena(&self) -> bool { + self.flags.contains(AllocationFlags::IN_NON_MAIN_ARENA) } #[inline] - pub fn in_main_arena( &self ) -> bool { + pub fn in_main_arena(&self) -> bool { !self.in_non_main_arena() } #[inline] - pub fn is_jemalloc( &self ) -> bool { - self.flags.contains( AllocationFlags::IS_JEMALLOC ) + pub fn is_jemalloc(&self) -> bool { + self.flags.contains(AllocationFlags::IS_JEMALLOC) } #[inline] - pub fn is_mmaped( &self ) -> bool { - self.flags.contains( AllocationFlags::IS_MMAPED ) + pub fn is_mmaped(&self) -> bool { + self.flags.contains(AllocationFlags::IS_MMAPED) } #[inline] - pub fn usable_size( &self ) -> u64 { + pub fn usable_size(&self) -> u64 { self.size + self.extra_usable_space as u64 } #[inline] - pub fn actual_range( &self, data: &Data ) -> Range< u64 > { + pub fn actual_range(&self, data: &Data) -> Range { let multiplier = if self.is_mmaped() { 2 } else { 1 }; - self.pointer - data.pointer_size * multiplier .. self.pointer + self.size + self.extra_usable_space as u64 + self.pointer - data.pointer_size * multiplier + ..self.pointer + self.size + self.extra_usable_space as u64 } } @@ -544,82 +572,92 @@ impl Allocation { pub struct Deallocation { pub timestamp: Timestamp, pub thread: ThreadId, - pub backtrace: Option< BacktraceId > + pub backtrace: Option, } #[derive(Debug)] -pub enum Operation< 'a > { +pub enum Operation<'a> { Allocation { allocation: &'a Allocation, - allocation_id: AllocationId + allocation_id: AllocationId, }, Deallocation { allocation: &'a Allocation, allocation_id: AllocationId, - deallocation: &'a Deallocation + deallocation: &'a Deallocation, }, Reallocation { allocation_id: AllocationId, new_allocation: &'a Allocation, deallocation: &'a Deallocation, - old_allocation: &'a Allocation - } + old_allocation: &'a Allocation, + }, } #[derive(Debug)] pub struct CountAndSize { pub count: u64, - pub size: u64 + pub size: u64, } #[inline] -fn binary_search_range< 'a, T, V, W, F >( array: &'a [T], min: Option< V >, max: Option< V >, callback: F ) -> Range< usize > - where V: Ord, - W: Borrow< V >, - F: Fn( &'a T ) -> W +fn binary_search_range<'a, T, V, W, F>( + array: &'a [T], + min: Option, + max: Option, + callback: F, +) -> Range +where + V: Ord, + W: Borrow, + F: Fn(&'a T) -> W, { if array.is_empty() { return 0..0; } - let start = if let Some( min ) = min { - if min > *callback( array.last().unwrap() ).borrow() { + let start = if let Some(min) = min { + if min > *callback(array.last().unwrap()).borrow() { return 0..0; } - if min <= *callback( array.first().unwrap() ).borrow() { + if min <= *callback(array.first().unwrap()).borrow() { 0 } else { - match array.binary_search_by( |key| callback( key ).borrow().cmp( &min ) ) { - Ok( mut index ) => { - while index > 0 && callback( &array[ index - 1 ] ).borrow().cmp( &min ) == Ordering::Equal { + match array.binary_search_by(|key| callback(key).borrow().cmp(&min)) { + Ok(mut index) => { + while index > 0 + && callback(&array[index - 1]).borrow().cmp(&min) == Ordering::Equal + { index -= 1; } index - }, - Err( index ) => index + } + Err(index) => index, } } } else { 0 }; - let end = if let Some( max ) = max { - if max < *callback( array.first().unwrap() ).borrow() { + let end = if let Some(max) = max { + if max < *callback(array.first().unwrap()).borrow() { return 0..0; } - if max >= *callback( array.last().unwrap() ).borrow() { + if max >= *callback(array.last().unwrap()).borrow() { array.len() } else { - match array.binary_search_by( |key| callback( key ).borrow().cmp( &max ) ) { - Ok( mut index ) => { - while index + 1 < array.len() && callback( &array[ index + 1 ] ).borrow().cmp( &max ) == Ordering::Equal { + match array.binary_search_by(|key| callback(key).borrow().cmp(&max)) { + Ok(mut index) => { + while index + 1 < array.len() + && callback(&array[index + 1]).borrow().cmp(&max) == Ordering::Equal + { index += 1; } index + 1 - }, - Err( index ) => index + } + Err(index) => index, } } } else { @@ -657,391 +695,464 @@ mod tests { #[test] fn test_binary_search_range() { assert_eq!( - binary_search_range( &[86, 87][..], None, Some( 86 ), |value| value ), + binary_search_range(&[86, 87][..], None, Some(86), |value| value), 0..1 ); assert_eq!( - binary_search_range( &[0, 80, 80][..], Some( 80 ), None, |value| value ), + binary_search_range(&[0, 80, 80][..], Some(80), None, |value| value), 1..3 ); assert_eq!( - binary_search_range( &[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2][..], Some( 1 ), Some( 1 ), |value| value ), + binary_search_range( + &[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2][..], + Some(1), + Some(1), + |value| value + ), 1..10 ); } } -pub trait SliceLikeIterator: DoubleEndedIterator + ExactSizeIterator + FusedIterator + Clone {} +pub trait SliceLikeIterator: + DoubleEndedIterator + ExactSizeIterator + FusedIterator + Clone +{ +} -impl< T > SliceLikeIterator for T - where T: DoubleEndedIterator + ExactSizeIterator + FusedIterator + Clone +impl SliceLikeIterator for T where + T: DoubleEndedIterator + ExactSizeIterator + FusedIterator + Clone { } impl Data { #[inline] - pub fn interner( &self ) -> &StringInterner { + pub fn interner(&self) -> &StringInterner { &self.interner } #[inline] - pub fn unsorted_allocations( &self ) -> impl SliceLikeIterator< Item = &Allocation > { + pub fn unsorted_allocations(&self) -> impl SliceLikeIterator { self.allocations.iter() } #[inline] - fn sorted_by< 'a, T, F >( + fn sorted_by<'a, T, F>( &'a self, array: &'a [AllocationId], - min: Option< T >, - max: Option< T >, - callback: F + min: Option, + max: Option, + callback: F, ) -> &'a [AllocationId] - where T: Ord + 'a, - F: Fn( &'a Allocation ) -> &T + where + T: Ord + 'a, + F: Fn(&'a Allocation) -> &T, { if min.is_none() && max.is_none() { return array; } - let range = binary_search_range( array, min, max, move |&id| callback( &self.allocations[ id.raw() as usize ] ) ); - &array[ range ] + let range = binary_search_range(array, min, max, move |&id| { + callback(&self.allocations[id.raw() as usize]) + }); + &array[range] } #[inline] - pub fn alloc_sorted_by_timestamp( &self, min: Option< Timestamp >, max: Option< Timestamp > ) -> &[AllocationId] { - self.sorted_by( &self.sorted_by_timestamp, min, max, |alloc| &alloc.timestamp ) + pub fn alloc_sorted_by_timestamp( + &self, + min: Option, + max: Option, + ) -> &[AllocationId] { + self.sorted_by(&self.sorted_by_timestamp, min, max, |alloc| { + &alloc.timestamp + }) } #[inline] - pub fn alloc_sorted_by_size( &self, min: Option< u64 >, max: Option< u64 > ) -> &[AllocationId] { - self.sorted_by( &self.sorted_by_size, min, max, |alloc| &alloc.size ) + pub fn alloc_sorted_by_size(&self, min: Option, max: Option) -> &[AllocationId] { + self.sorted_by(&self.sorted_by_size, min, max, |alloc| &alloc.size) } #[inline] - pub fn alloc_sorted_by_address( &self, min: Option< u64 >, max: Option< u64 > ) -> &[AllocationId] { - self.sorted_by( &self.sorted_by_address, min, max, |alloc| &alloc.pointer ) + pub fn alloc_sorted_by_address(&self, min: Option, max: Option) -> &[AllocationId] { + self.sorted_by(&self.sorted_by_address, min, max, |alloc| &alloc.pointer) } #[inline] - pub fn allocations_with_id( &self ) -> impl Iterator< Item = (AllocationId, &Allocation) > { - self.allocations.iter().enumerate().map( |(index, allocation)| (AllocationId::new( index as _ ), allocation) ) + pub fn allocations_with_id(&self) -> impl Iterator { + self.allocations + .iter() + .enumerate() + .map(|(index, allocation)| (AllocationId::new(index as _), allocation)) } - pub fn maps( &self ) -> &[Map] { + pub fn maps(&self) -> &[Map] { &self.maps } - pub fn operation_ids( &self ) -> &[OperationId] { + pub fn operation_ids(&self) -> &[OperationId] { &self.operations } #[inline] - pub fn operations< 'a >( &'a self ) -> impl Iterator< Item = Operation< 'a > > + 'a { - self.operations.iter().map( move |op| { + pub fn operations<'a>(&'a self) -> impl Iterator> + 'a { + self.operations.iter().map(move |op| { let allocation_id = op.id(); - let allocation = &self.allocations[ allocation_id.raw() as usize ]; + let allocation = &self.allocations[allocation_id.raw() as usize]; if op.is_allocation() { Operation::Allocation { allocation, - allocation_id + allocation_id, } } else if op.is_reallocation() { - let previous_allocation = &self.allocations[ allocation.reallocated_from.unwrap().raw() as usize ]; + let previous_allocation = + &self.allocations[allocation.reallocated_from.unwrap().raw() as usize]; Operation::Reallocation { allocation_id, new_allocation: allocation, deallocation: previous_allocation.deallocation.as_ref().unwrap(), - old_allocation: previous_allocation + old_allocation: previous_allocation, } } else { Operation::Deallocation { allocation, allocation_id, - deallocation: allocation.deallocation.as_ref().unwrap() + deallocation: allocation.deallocation.as_ref().unwrap(), } } }) } - pub fn total_allocated( &self ) -> u64 { + pub fn total_allocated(&self) -> u64 { self.total_allocated } - pub fn total_allocated_count( &self ) -> u64 { + pub fn total_allocated_count(&self) -> u64 { self.total_allocated_count } - pub fn total_freed( &self ) -> u64 { + pub fn total_freed(&self) -> u64 { self.total_freed } - pub fn total_freed_count( &self ) -> u64 { + pub fn total_freed_count(&self) -> u64 { self.total_freed_count } #[inline] - pub fn initial_timestamp( &self ) -> Timestamp { + pub fn initial_timestamp(&self) -> Timestamp { self.initial_timestamp } #[inline] - pub fn last_timestamp( &self ) -> Timestamp { + pub fn last_timestamp(&self) -> Timestamp { self.last_timestamp } #[inline] - pub fn pointer_size( &self ) -> u64 { + pub fn pointer_size(&self) -> u64 { self.pointer_size } #[inline] - pub fn executable( &self ) -> &str { + pub fn executable(&self) -> &str { &self.executable } #[inline] - pub fn cmdline( &self ) -> Vec< String > { - self.cmdline.split( "\0" ).map( |arg| arg.to_owned() ).collect() + pub fn cmdline(&self) -> Vec { + self.cmdline.split("\0").map(|arg| arg.to_owned()).collect() } #[inline] - pub fn architecture( &self ) -> &str { + pub fn architecture(&self) -> &str { &self.architecture } #[inline] - pub fn id( &self ) -> DataId { + pub fn id(&self) -> DataId { self.id } #[inline] - pub fn unique_backtrace_count( &self ) -> usize { + pub fn unique_backtrace_count(&self) -> usize { self.backtraces.len() } #[inline] - pub fn maximum_backtrace_depth( &self ) -> u32 { + pub fn maximum_backtrace_depth(&self) -> u32 { self.maximum_backtrace_depth } #[inline] - pub fn get_frame_ids( &self, id: BacktraceId ) -> &[FrameId] { - let (offset, length) = self.backtraces[ id.0 as usize ]; - &self.backtraces_storage[ (offset as usize)..(offset + length) as usize ] + pub fn get_frame_ids(&self, id: BacktraceId) -> &[FrameId] { + let (offset, length) = self.backtraces[id.0 as usize]; + &self.backtraces_storage[(offset as usize)..(offset + length) as usize] } - pub fn get_frame( &self, id: FrameId ) -> &Frame { - &self.frames[ id ] + pub fn get_frame(&self, id: FrameId) -> &Frame { + &self.frames[id] } - pub fn get_chain_by_first_allocation( &self, id: AllocationId ) -> Option< &AllocationChain > { - self.chains.get( &id ) + pub fn get_chain_by_first_allocation(&self, id: AllocationId) -> Option<&AllocationChain> { + self.chains.get(&id) } - pub fn get_chain_by_any_allocation( &self, id: AllocationId ) -> AllocationChain { - let alloc = self.get_allocation( id ); - if let Some( initial ) = alloc.first_allocation_in_chain { - self.chains.get( &initial ).unwrap().clone() + pub fn get_chain_by_any_allocation(&self, id: AllocationId) -> AllocationChain { + let alloc = self.get_allocation(id); + if let Some(initial) = alloc.first_allocation_in_chain { + self.chains.get(&initial).unwrap().clone() } else { AllocationChain { first: id, last: id, - length: 1 + length: 1, } } } - pub fn get_allocation( &self, id: AllocationId ) -> &Allocation { - &self.allocations[ id.raw() as usize ] + pub fn get_allocation(&self, id: AllocationId) -> &Allocation { + &self.allocations[id.raw() as usize] } - pub fn get_allocations_by_backtrace( &self, id: BacktraceId ) -> impl SliceLikeIterator< Item = (AllocationId, &Allocation) > { - self.allocations_by_backtrace.get( id.raw() as _ ).iter().map( move |&allocation_id| (allocation_id, &self.allocations[ allocation_id.raw() as usize ]) ) + pub fn get_allocations_by_backtrace( + &self, + id: BacktraceId, + ) -> impl SliceLikeIterator { + self.allocations_by_backtrace + .get(id.raw() as _) + .iter() + .map(move |&allocation_id| { + ( + allocation_id, + &self.allocations[allocation_id.raw() as usize], + ) + }) } - pub fn get_allocation_ids_by_backtrace( &self, id: BacktraceId ) -> &[AllocationId] { - self.allocations_by_backtrace.get( id.raw() as _ ) + pub fn get_allocation_ids_by_backtrace(&self, id: BacktraceId) -> &[AllocationId] { + self.allocations_by_backtrace.get(id.raw() as _) } - pub fn get_map( &self, id: MapId ) -> &Map { - &self.maps[ id.raw() as usize ] + pub fn get_map(&self, id: MapId) -> &Map { + &self.maps[id.raw() as usize] } - pub fn contains_map( &self, id: MapId ) -> bool { + pub fn contains_map(&self, id: MapId) -> bool { (id.0 as usize) < self.maps.len() } - pub fn get_backtrace< 'a >( &'a self, id: BacktraceId ) -> impl SliceLikeIterator< Item = (FrameId, &'a Frame) > + Clone { - self.get_frame_ids( id ).iter().rev().map( move |&frame_id| (frame_id, &self.frames[ frame_id ]) ) + pub fn get_backtrace<'a>( + &'a self, + id: BacktraceId, + ) -> impl SliceLikeIterator + Clone { + self.get_frame_ids(id) + .iter() + .rev() + .map(move |&frame_id| (frame_id, &self.frames[frame_id])) } - pub fn get_group_statistics( &self, id: BacktraceId ) -> &GroupStatistics { - &self.group_stats[ id.raw() as usize ] + pub fn get_group_statistics(&self, id: BacktraceId) -> &GroupStatistics { + &self.group_stats[id.raw() as usize] } - pub fn all_backtraces< 'a >( &'a self ) -> - impl SliceLikeIterator< - Item = ( - BacktraceId, - impl SliceLikeIterator< - Item = (FrameId, &'a Frame) - > - ) - > - { - (0..self.backtraces.len()).into_iter().map( move |id| { - let id = BacktraceId::new( id as _ ); - (id, self.get_backtrace( id )) + pub fn all_backtraces<'a>( + &'a self, + ) -> impl SliceLikeIterator< + Item = ( + BacktraceId, + impl SliceLikeIterator, + ), + > { + (0..self.backtraces.len()).into_iter().map(move |id| { + let id = BacktraceId::new(id as _); + (id, self.get_backtrace(id)) }) } - pub fn get_non_inline_backtrace< 'a >( &'a self, id: BacktraceId ) -> impl Iterator< Item = (FrameId, &'a Frame) > + FusedIterator + DoubleEndedIterator { + pub fn get_non_inline_backtrace<'a>( + &'a self, + id: BacktraceId, + ) -> impl Iterator + FusedIterator + DoubleEndedIterator { let mut last_address = None; - self.get_backtrace( id ).filter_map( move |(frame_id, frame)| { - let ok = last_address.map( |last_address| last_address != frame.address() ).unwrap_or( true ); - last_address = Some( frame.address() ); + self.get_backtrace(id).filter_map(move |(frame_id, frame)| { + let ok = last_address + .map(|last_address| last_address != frame.address()) + .unwrap_or(true); + last_address = Some(frame.address()); if ok { - Some( (frame_id, frame) ) + Some((frame_id, frame)) } else { None } }) } - pub fn raw_tree( &self ) -> Tree< CodePointer, FrameId > { + pub fn raw_tree(&self) -> Tree { let mut tree = Tree::new(); for (allocation_id, allocation) in self.allocations_with_id() { if allocation.was_deallocated() { continue; } - tree.add_allocation( &allocation, allocation_id, self.get_non_inline_backtrace( allocation.backtrace ).map( |(frame_id, frame)| { - (frame.address(), frame_id) - })); + tree.add_allocation( + &allocation, + allocation_id, + self.get_non_inline_backtrace(allocation.backtrace) + .map(|(frame_id, frame)| (frame.address(), frame_id)), + ); } tree } - pub fn tree_by_source< F >( &self, filter: F ) -> Tree< SourceKey, FrameId > where F: Fn( AllocationId, &Allocation ) -> bool { + pub fn tree_by_source(&self, filter: F) -> Tree + where + F: Fn(AllocationId, &Allocation) -> bool, + { let mut tree = Tree::new(); for (allocation_id, allocation) in self.allocations_with_id() { - if !filter( allocation_id, allocation ) { + if !filter(allocation_id, allocation) { continue; } - tree.add_allocation( &allocation, allocation_id, self.get_backtrace( allocation.backtrace ).map( |(frame_id, frame)| { - let key = match (frame.source(), frame.line(), frame.function().or( frame.raw_function() )) { - (Some( source ), Some( line ), _) => SourceKey::Location( source, line ), - (_, _, Some( function )) => SourceKey::Function( function ), - _ => SourceKey::Address( frame.address() ) - }; - - (key, frame_id) - })); + tree.add_allocation( + &allocation, + allocation_id, + self.get_backtrace(allocation.backtrace) + .map(|(frame_id, frame)| { + let key = match ( + frame.source(), + frame.line(), + frame.function().or(frame.raw_function()), + ) { + (Some(source), Some(line), _) => SourceKey::Location(source, line), + (_, _, Some(function)) => SourceKey::Function(function), + _ => SourceKey::Address(frame.address()), + }; + + (key, frame_id) + }), + ); } tree } - pub fn dump_tree( &self, tree: &Tree< SourceKey, FrameId > ) -> Vec< Vec< String > > { - dump_tree( &tree, self.initial_timestamp, |&frame_id| { - let frame = &self.frames[ frame_id ]; - if let Some( function ) = frame.any_function() { - let function = self.interner.resolve( function ).unwrap(); - if let (Some( source ), Some( line )) = (frame.source(), frame.line()) { - let source = self.interner.resolve( source ).unwrap(); - let filename = &source[ source.rfind( "/" ).map( |index| index + 1 ).unwrap_or( 0 ).. ]; - format!( "{} [{}:{}]", function, filename, line ) + pub fn dump_tree(&self, tree: &Tree) -> Vec> { + dump_tree(&tree, self.initial_timestamp, |&frame_id| { + let frame = &self.frames[frame_id]; + if let Some(function) = frame.any_function() { + let function = self.interner.resolve(function).unwrap(); + if let (Some(source), Some(line)) = (frame.source(), frame.line()) { + let source = self.interner.resolve(source).unwrap(); + let filename = &source[source.rfind("/").map(|index| index + 1).unwrap_or(0)..]; + format!("{} [{}:{}]", function, filename, line) } else { - format!( "{}", function ) + format!("{}", function) } - } else if let Some( library ) = frame.library() { - format!( "{} [{}]", frame.address(), self.interner.resolve( library ).unwrap() ) + } else if let Some(library) = frame.library() { + format!( + "{} [{}]", + frame.address(), + self.interner.resolve(library).unwrap() + ) } else { - format!( "{}", frame.address() ) + format!("{}", frame.address()) } }) } - pub fn mallopts( &self ) -> &[Mallopt] { + pub fn mallopts(&self) -> &[Mallopt] { &self.mallopts } - pub fn get_dynamic_constants( &self ) -> BTreeMap< String, BTreeMap< u32, CountAndSize > > { - self.collate_allocations( |frame| { + pub fn get_dynamic_constants(&self) -> BTreeMap> { + self.collate_allocations(|frame| { let raw_function = match frame.raw_function() { - Some( raw_function ) => raw_function, - None => return false + Some(raw_function) => raw_function, + None => return false, }; - let raw_function = self.interner().resolve( raw_function ).unwrap(); - raw_function.contains( "__static_initialization_and_destruction_0" ) && raw_function.contains( ".constprop" ) + let raw_function = self.interner().resolve(raw_function).unwrap(); + raw_function.contains("__static_initialization_and_destruction_0") + && raw_function.contains(".constprop") }) } - pub fn get_dynamic_constants_ascii_tree( &self ) -> String { + pub fn get_dynamic_constants_ascii_tree(&self) -> String { let constants = self.get_dynamic_constants(); - self.collation_to_ascii_tree( constants ) + self.collation_to_ascii_tree(constants) } - pub fn get_dynamic_statics( &self ) -> BTreeMap< String, BTreeMap< u32, CountAndSize > > { - self.collate_allocations( |frame| { + pub fn get_dynamic_statics(&self) -> BTreeMap> { + self.collate_allocations(|frame| { let raw_function = match frame.raw_function() { - Some( raw_function ) => raw_function, - None => return false + Some(raw_function) => raw_function, + None => return false, }; - let raw_function = self.interner().resolve( raw_function ).unwrap(); - raw_function.contains( "__static_initialization_and_destruction_0" ) && !raw_function.contains( ".constprop" ) + let raw_function = self.interner().resolve(raw_function).unwrap(); + raw_function.contains("__static_initialization_and_destruction_0") + && !raw_function.contains(".constprop") }) } - pub fn get_dynamic_statics_ascii_tree( &self ) -> String { + pub fn get_dynamic_statics_ascii_tree(&self) -> String { let constants = self.get_dynamic_statics(); - self.collation_to_ascii_tree( constants ) + self.collation_to_ascii_tree(constants) } - fn collate_allocations< F >( &self, filter: F ) -> BTreeMap< String, BTreeMap< u32, CountAndSize > > - where F: Fn( &Frame ) -> bool + fn collate_allocations(&self, filter: F) -> BTreeMap> + where + F: Fn(&Frame) -> bool, { let mut backtrace_to_src = HashMap::new(); for (backtrace_id, frames) in self.all_backtraces() { for (_, frame) in frames { - if !filter( frame ) { + if !filter(frame) { continue; } let source = match frame.source() { - Some( source ) => source, - None => continue + Some(source) => source, + None => continue, }; let line = match frame.line() { - Some( line ) => line, - None => continue + Some(line) => line, + None => continue, }; - let mut source: Cow< str > = self.interner().resolve( source ).unwrap().into(); + let mut source: Cow = self.interner().resolve(source).unwrap().into(); // We need this so that we can properly collapse the paths to the same file. const DISTCC_PATTERN: &'static str = "/distccd_"; - if let Some( index ) = source.find( DISTCC_PATTERN ) { - if source[ index.. ].chars().skip( DISTCC_PATTERN.len() + 6 ).next() == Some( '/' ) { - source = format!( "{}/distccd_XXXXXX{}", &source[ ..index ], &source[ index + DISTCC_PATTERN.len() + 6.. ] ).into(); + if let Some(index) = source.find(DISTCC_PATTERN) { + if source[index..] + .chars() + .skip(DISTCC_PATTERN.len() + 6) + .next() + == Some('/') + { + source = format!( + "{}/distccd_XXXXXX{}", + &source[..index], + &source[index + DISTCC_PATTERN.len() + 6..] + ) + .into(); } } const DISTCC_STANDARD_PREFIX: &'static str = "/dev/shm/distcc/distccd_XXXXXX"; - if source.starts_with( DISTCC_STANDARD_PREFIX ) { - source = format!( "{}", &source[ DISTCC_STANDARD_PREFIX.len().. ] ).into(); + if source.starts_with(DISTCC_STANDARD_PREFIX) { + source = format!("{}", &source[DISTCC_STANDARD_PREFIX.len()..]).into(); } - backtrace_to_src.insert( backtrace_id, (source, line) ); + backtrace_to_src.insert(backtrace_id, (source, line)); break; } } @@ -1052,124 +1163,134 @@ impl Data { continue; } - let src = match backtrace_to_src.get( &allocation.backtrace ).cloned() { - Some( src ) => src, - None => continue + let src = match backtrace_to_src.get(&allocation.backtrace).cloned() { + Some(src) => src, + None => continue, }; let (source, line) = src; - let per_line = per_file.entry( source ).or_insert_with( || BTreeMap::new() ); - let stats = per_line.entry( line ).or_insert( CountAndSize { count: 0, size: 0 } ); + let per_line = per_file.entry(source).or_insert_with(|| BTreeMap::new()); + let stats = per_line + .entry(line) + .or_insert(CountAndSize { count: 0, size: 0 }); stats.count += 1; stats.size += allocation.usable_size(); } - per_file.into_iter().map( |(key_id, value)| { - let key = key_id.into_owned(); - (key, value) - }).collect() + per_file + .into_iter() + .map(|(key_id, value)| { + let key = key_id.into_owned(); + (key, value) + }) + .collect() } - fn collation_to_ascii_tree( &self, collation: BTreeMap< String, BTreeMap< u32, CountAndSize > > ) -> String { + fn collation_to_ascii_tree( + &self, + collation: BTreeMap>, + ) -> String { let mut total_count = 0; let mut total_size = 0; let mut row_count = 0; - let mut collation: Vec< _ > = collation.into_iter().map( |(source, per_line)| { - let mut whole_file_count = 0; - let mut whole_file_size = 0; - for (_, entry) in &per_line { - whole_file_count += entry.count; - whole_file_size += entry.size; - } - total_count += whole_file_count; - total_size += whole_file_size; - row_count = per_line.len() + 1; - (whole_file_size, whole_file_count, source, per_line) - }).collect(); - - collation.sort_by( |a, b| { - b.0.cmp( &a.0 ) - }); + let mut collation: Vec<_> = collation + .into_iter() + .map(|(source, per_line)| { + let mut whole_file_count = 0; + let mut whole_file_size = 0; + for (_, entry) in &per_line { + whole_file_count += entry.count; + whole_file_size += entry.size; + } + total_count += whole_file_count; + total_size += whole_file_size; + row_count = per_line.len() + 1; + (whole_file_size, whole_file_count, source, per_line) + }) + .collect(); - let mut table = Vec::with_capacity( row_count ); - table.push( vec![ + collation.sort_by(|a, b| b.0.cmp(&a.0)); + + let mut table = Vec::with_capacity(row_count); + table.push(vec![ "SIZE".to_owned(), "COUNT".to_owned(), - "SOURCE".to_owned() + "SOURCE".to_owned(), ]); - table.push( vec![ - format!( "{}", ReadableSize( total_size ) ), - format!( "{}", total_count ), - "▒".to_owned() + table.push(vec![ + format!("{}", ReadableSize(total_size)), + format!("{}", total_count), + "▒".to_owned(), ]); let mut sorted_per_line = Vec::new(); let tree_count = collation.len(); - for (index, (whole_file_size, whole_file_count, source, per_line)) in collation.into_iter().enumerate() { + for (index, (whole_file_size, whole_file_count, source, per_line)) in + collation.into_iter().enumerate() + { let is_last_per_file = index + 1 == tree_count; - let filename = &source[ source.rfind( "/" ).map( |index| index + 1 ).unwrap_or( 0 ).. ]; + let filename = &source[source.rfind("/").map(|index| index + 1).unwrap_or(0)..]; if per_line.len() == 1 { let line = per_line.into_iter().next().unwrap().0; - table.push( vec![ - format!( "{}", ReadableSize( whole_file_size ) ), - format!( "{}", whole_file_count ), + table.push(vec![ + format!("{}", ReadableSize(whole_file_size)), + format!("{}", whole_file_count), format!( "{}─ {}:{} [{}]", if is_last_per_file { ' ' } else { '|' }, filename, line, source - ) + ), ]); continue; } - table.push( vec![ - format!( "{}", ReadableSize( whole_file_size ) ), - format!( "{}", whole_file_count ), + table.push(vec![ + format!("{}", ReadableSize(whole_file_size)), + format!("{}", whole_file_count), format!( "{}─ {} [{}]", if is_last_per_file { '└' } else { '├' }, filename, source - ) + ), ]); - sorted_per_line.extend( per_line.into_iter() ); - sorted_per_line.sort_by( |a, b| { - (b.1).size.cmp( &(a.1).size ) - }); + sorted_per_line.extend(per_line.into_iter()); + sorted_per_line.sort_by(|a, b| (b.1).size.cmp(&(a.1).size)); let subtree_count = sorted_per_line.len(); - for (index, (line, entry)) in sorted_per_line.drain( .. ).enumerate() { + for (index, (line, entry)) in sorted_per_line.drain(..).enumerate() { let is_last_per_line = index + 1 == subtree_count; - table.push( vec![ - format!( "{}", ReadableSize( entry.size ) ), - format!( "{}", entry.count ), + table.push(vec![ + format!("{}", ReadableSize(entry.size)), + format!("{}", entry.count), format!( "{} {}─ {}:{}", if is_last_per_file { ' ' } else { '|' }, if is_last_per_line { '└' } else { '├' }, filename, line - ) + ), ]); } } - table_to_string( &table ) + table_to_string(&table) } } impl AllocationChain { - pub fn lifetime( &self, data: &Data ) -> Option< Timestamp > { + pub fn lifetime(&self, data: &Data) -> Option { Some( - data.get_allocation( self.last ) - .deallocation.as_ref() - .map( |deallocation| deallocation.timestamp )? - - data.get_allocation( self.first ).timestamp + data.get_allocation(self.last) + .deallocation + .as_ref() + .map(|deallocation| deallocation.timestamp)? + - data.get_allocation(self.first).timestamp, ) } } diff --git a/cli-core/src/exporter_flamegraph.rs b/cli-core/src/exporter_flamegraph.rs index 0fd7467c..ec0f0e9d 100644 --- a/cli-core/src/exporter_flamegraph.rs +++ b/cli-core/src/exporter_flamegraph.rs @@ -3,49 +3,54 @@ use std::sync::Mutex; use inferno::flamegraph; -use super::{ - Allocation, - AllocationId, - Data -}; +use super::{Allocation, AllocationId, Data}; use crate::exporter_flamegraph_pl::dump_collation; use crate::io_adapter::IoAdapter; -pub fn lines_to_svg( lines: Vec< String >, output: impl fmt::Write ) { +pub fn lines_to_svg(lines: Vec, output: impl fmt::Write) { lazy_static::lazy_static! { pub static ref PALETTE_MAP: Mutex< flamegraph::color::PaletteMap > = Mutex::new( flamegraph::color::PaletteMap::default() ); } let mut options = flamegraph::Options::default(); - options.colors = flamegraph::color::Palette::Basic( flamegraph::color::BasicPalette::Mem ); - options.bgcolors = Some( flamegraph::color::BackgroundColor::Flat( (255, 255, 255).into() ) ); - options.font_type = r#""Segoe UI", "Source Sans Pro", Calibri, Candara, Arial, sans-serif"#.to_owned(); + options.colors = flamegraph::color::Palette::Basic(flamegraph::color::BasicPalette::Mem); + options.bgcolors = Some(flamegraph::color::BackgroundColor::Flat( + (255, 255, 255).into(), + )); + options.font_type = + r#""Segoe UI", "Source Sans Pro", Calibri, Candara, Arial, sans-serif"#.to_owned(); options.title = "".to_owned(); options.count_name = "bytes".to_owned(); let mut palette_map = PALETTE_MAP.lock(); - if let Ok( ref mut palette_map ) = palette_map { - options.palette_map = Some( palette_map ); + if let Ok(ref mut palette_map) = palette_map { + options.palette_map = Some(palette_map); } // We explicitly ignore the error to prevent a panic in case // we didn't match any allocations. - let _ = flamegraph::from_lines( &mut options, lines.iter().map( |line| line.as_str() ), IoAdapter::new( output ) ); + let _ = flamegraph::from_lines( + &mut options, + lines.iter().map(|line| line.as_str()), + IoAdapter::new(output), + ); } -pub fn export_as_flamegraph< T, F >( data: &Data, output: T, filter: F ) - where T: fmt::Write, - F: Fn( AllocationId, &Allocation ) -> bool +pub fn export_as_flamegraph(data: &Data, output: T, filter: F) +where + T: fmt::Write, + F: Fn(AllocationId, &Allocation) -> bool, { let mut lines = Vec::new(); - dump_collation( data, filter, |line| { - lines.push( line.to_owned() ); - let result: Result< (), () > = Ok(()); + dump_collation(data, filter, |line| { + lines.push(line.to_owned()); + let result: Result<(), ()> = Ok(()); result - }).unwrap(); + }) + .unwrap(); lines.sort_unstable(); - lines_to_svg( lines, output ) + lines_to_svg(lines, output) } diff --git a/cli-core/src/exporter_flamegraph_pl.rs b/cli-core/src/exporter_flamegraph_pl.rs index 5037d153..51af712c 100644 --- a/cli-core/src/exporter_flamegraph_pl.rs +++ b/cli-core/src/exporter_flamegraph_pl.rs @@ -1,85 +1,112 @@ -use super::{ - Allocation, - AllocationId, - Data, - Frame, - FrameId, - NodeId, - Tree -}; +use super::{Allocation, AllocationId, Data, Frame, FrameId, NodeId, Tree}; use std::fmt::{self, Write}; -fn dump_collation_impl< O: FnMut( &str ) -> Result< (), E >, K: PartialEq + Clone, E >( +fn dump_collation_impl Result<(), E>, K: PartialEq + Clone, E>( data: &Data, - tree: &Tree< K, &Frame >, + tree: &Tree, node_id: NodeId, - stack: &mut Vec< String >, - cache: &mut Vec< String >, - output: &mut O -) -> Result< (), E > { - let node = tree.get_node( node_id ); + stack: &mut Vec, + cache: &mut Vec, + output: &mut O, +) -> Result<(), E> { + let node = tree.get_node(node_id); - if let Some( value ) = node.value() { - let mut buffer = cache.pop().unwrap_or( String::new() ); - let library = value.library().map( |id| data.interner().resolve( id ).unwrap() ).unwrap_or( "???" ); - if let Some( function ) = value.function().map( |id| data.interner().resolve( id ).unwrap() ) { - write!( &mut buffer, "{} [{}]", function, library ).unwrap(); - } else if let Some( function ) = value.raw_function().map( |id| data.interner().resolve( id ).unwrap() ) { - write!( &mut buffer, "{} [{}]", function, library ).unwrap(); + if let Some(value) = node.value() { + let mut buffer = cache.pop().unwrap_or(String::new()); + let library = value + .library() + .map(|id| data.interner().resolve(id).unwrap()) + .unwrap_or("???"); + if let Some(function) = value + .function() + .map(|id| data.interner().resolve(id).unwrap()) + { + write!(&mut buffer, "{} [{}]", function, library).unwrap(); + } else if let Some(function) = value + .raw_function() + .map(|id| data.interner().resolve(id).unwrap()) + { + write!(&mut buffer, "{} [{}]", function, library).unwrap(); } else { - write!( &mut buffer, "0x{:016X} [{}]", value.address().raw(), library ).unwrap(); + write!( + &mut buffer, + "0x{:016X} [{}]", + value.address().raw(), + library + ) + .unwrap(); } - stack.push( buffer ); + stack.push(buffer); } if node.self_count != 0 { - let mut buffer = cache.pop().unwrap_or( String::new() ); - write!( &mut buffer, "{} {}", stack.join( ";" ), node.self_size ).unwrap(); + let mut buffer = cache.pop().unwrap_or(String::new()); + write!(&mut buffer, "{} {}", stack.join(";"), node.self_size).unwrap(); - output( &buffer )?; + output(&buffer)?; buffer.clear(); - cache.push( buffer ); + cache.push(buffer); } - for &(_, child_id) in tree.get_node( node_id ).children.iter() { - dump_collation_impl( data, tree, child_id, stack, cache, output )?; + for &(_, child_id) in tree.get_node(node_id).children.iter() { + dump_collation_impl(data, tree, child_id, stack, cache, output)?; } if !node.is_root() { let mut buffer = stack.pop().unwrap(); buffer.clear(); - cache.push( buffer ); + cache.push(buffer); } Ok(()) } -pub fn dump_collation_from_iter< 'a, O, E >( +pub fn dump_collation_from_iter<'a, O, E>( data: &Data, - allocations: impl Iterator< Item = (AllocationId, &'a Allocation) >, - mut output: O -) -> Result< (), E > - where O: FnMut( &str ) -> Result< (), E > + allocations: impl Iterator, + mut output: O, +) -> Result<(), E> +where + O: FnMut(&str) -> Result<(), E>, { - let mut tree: Tree< FrameId, &Frame > = Tree::new(); + let mut tree: Tree = Tree::new(); for (allocation_id, allocation) in allocations { - tree.add_allocation( allocation, allocation_id, data.get_backtrace( allocation.backtrace ) ); + tree.add_allocation( + allocation, + allocation_id, + data.get_backtrace(allocation.backtrace), + ); } - dump_collation_impl( data, &tree, 0, &mut Vec::new(), &mut Vec::new(), &mut output ) + dump_collation_impl( + data, + &tree, + 0, + &mut Vec::new(), + &mut Vec::new(), + &mut output, + ) } -pub fn dump_collation< F, O, E >( data: &Data, filter: F, output: O ) -> Result< (), E > - where F: Fn( AllocationId, &Allocation ) -> bool, - O: FnMut( &str ) -> Result< (), E > +pub fn dump_collation(data: &Data, filter: F, output: O) -> Result<(), E> +where + F: Fn(AllocationId, &Allocation) -> bool, + O: FnMut(&str) -> Result<(), E>, { - dump_collation_from_iter( data, data.allocations_with_id().filter( |(id, allocation)| filter( *id, allocation ) ), output ) + dump_collation_from_iter( + data, + data.allocations_with_id() + .filter(|(id, allocation)| filter(*id, allocation)), + output, + ) } -pub fn export_as_flamegraph_pl< T: fmt::Write, F: Fn( AllocationId, &Allocation ) -> bool >( data: &Data, mut output: T, filter: F ) -> fmt::Result { - dump_collation( data, filter, |line| { - writeln!( &mut output, "{}", line ) - }) +pub fn export_as_flamegraph_pl bool>( + data: &Data, + mut output: T, + filter: F, +) -> fmt::Result { + dump_collation(data, filter, |line| writeln!(&mut output, "{}", line)) } diff --git a/cli-core/src/exporter_heaptrack.rs b/cli-core/src/exporter_heaptrack.rs index 9f279bfd..d3db4884 100644 --- a/cli-core/src/exporter_heaptrack.rs +++ b/cli-core/src/exporter_heaptrack.rs @@ -1,17 +1,9 @@ -use std::io; -use std::fmt; use std::collections::HashMap; +use std::fmt; +use std::io; use super::{ - Allocation, - AllocationId, - BacktraceId, - CodePointer, - Data, - Frame, - StringId, - Timestamp, - Operation + Allocation, AllocationId, BacktraceId, CodePointer, Data, Frame, Operation, StringId, Timestamp, }; use crate::io_adapter::IoAdapter; @@ -19,27 +11,27 @@ use crate::io_adapter::IoAdapter; #[derive(PartialEq, Eq, Hash)] struct AllocInfo { size: u64, - backtrace: usize + backtrace: usize, } struct Child { ip_index: usize, - trace_node_index: usize + trace_node_index: usize, } struct TraceNode { - children: Vec< Child > + children: Vec, } -pub struct HeaptrackExporter< 'a, T: fmt::Write > { - alloc_info_to_index: HashMap< AllocInfo, usize >, - backtrace_to_index: HashMap< BacktraceId, usize >, - ip_to_index: HashMap< CodePointer, usize >, - string_map: HashMap< StringId, usize >, - trace_tree: Vec< TraceNode >, +pub struct HeaptrackExporter<'a, T: fmt::Write> { + alloc_info_to_index: HashMap, + backtrace_to_index: HashMap, + ip_to_index: HashMap, + string_map: HashMap, + trace_tree: Vec, tx: T, data: &'a Data, - last_elapsed: Timestamp + last_elapsed: Timestamp, } /* @@ -62,80 +54,80 @@ pub struct HeaptrackExporter< 'a, T: fmt::Write > { s */ -impl< 'a, T: fmt::Write > HeaptrackExporter< 'a, T > { - fn new( data: &'a Data, mut tx: T ) -> Result< Self, fmt::Error > { - writeln!( tx, "v 10100 2" )?; - writeln!( tx, "X {}", data.executable() )?; +impl<'a, T: fmt::Write> HeaptrackExporter<'a, T> { + fn new(data: &'a Data, mut tx: T) -> Result { + writeln!(tx, "v 10100 2")?; + writeln!(tx, "X {}", data.executable())?; let exporter = HeaptrackExporter { alloc_info_to_index: HashMap::new(), backtrace_to_index: HashMap::new(), ip_to_index: HashMap::new(), string_map: HashMap::new(), - trace_tree: vec![ TraceNode { - children: Vec::new() + trace_tree: vec![TraceNode { + children: Vec::new(), }], tx, data, - last_elapsed: Timestamp::min() + last_elapsed: Timestamp::min(), }; - Ok( exporter ) + Ok(exporter) } - fn emit_timestamp( &mut self, timestamp: Timestamp ) -> Result< (), fmt::Error > { + fn emit_timestamp(&mut self, timestamp: Timestamp) -> Result<(), fmt::Error> { let elapsed = timestamp - self.data.initial_timestamp(); if self.last_elapsed != elapsed { - writeln!( self.tx, "c {:x}", elapsed.as_msecs() )?; + writeln!(self.tx, "c {:x}", elapsed.as_msecs())?; self.last_elapsed = elapsed; } Ok(()) } - fn get_size( &self, allocation: &Allocation ) -> u64 { + fn get_size(&self, allocation: &Allocation) -> u64 { allocation.size + allocation.extra_usable_space as u64 } - pub fn handle_alloc( &mut self, allocation: &Allocation ) -> Result< (), fmt::Error > { + pub fn handle_alloc(&mut self, allocation: &Allocation) -> Result<(), fmt::Error> { let alloc_info = AllocInfo { - size: self.get_size( allocation ), - backtrace: self.resolve_backtrace( allocation.backtrace )? + size: self.get_size(allocation), + backtrace: self.resolve_backtrace(allocation.backtrace)?, }; - self.emit_timestamp( allocation.timestamp )?; + self.emit_timestamp(allocation.timestamp)?; - let alloc_info_index = self.resolve_alloc_info( alloc_info )?; - writeln!( self.tx, "+ {:x}", alloc_info_index ) + let alloc_info_index = self.resolve_alloc_info(alloc_info)?; + writeln!(self.tx, "+ {:x}", alloc_info_index) } - pub fn handle_dealloc( &mut self, allocation: &Allocation ) -> Result< (), fmt::Error > { + pub fn handle_dealloc(&mut self, allocation: &Allocation) -> Result<(), fmt::Error> { let alloc_info = AllocInfo { - size: self.get_size( allocation ), - backtrace: self.resolve_backtrace( allocation.backtrace )? + size: self.get_size(allocation), + backtrace: self.resolve_backtrace(allocation.backtrace)?, }; - self.emit_timestamp( allocation.timestamp )?; + self.emit_timestamp(allocation.timestamp)?; - let alloc_info_index = self.alloc_info_to_index.get( &alloc_info ).unwrap(); - writeln!( self.tx, "- {:x}", alloc_info_index ) + let alloc_info_index = self.alloc_info_to_index.get(&alloc_info).unwrap(); + writeln!(self.tx, "- {:x}", alloc_info_index) } - fn resolve_backtrace( &mut self, backtrace_id: BacktraceId ) -> Result< usize, fmt::Error > { - if let Some( &index ) = self.backtrace_to_index.get( &backtrace_id ) { - return Ok( index ); + fn resolve_backtrace(&mut self, backtrace_id: BacktraceId) -> Result { + if let Some(&index) = self.backtrace_to_index.get(&backtrace_id) { + return Ok(index); } let mut parent_trace_index = 0; - let frame_ids = self.data.get_frame_ids( backtrace_id ); + let frame_ids = self.data.get_frame_ids(backtrace_id); if frame_ids.is_empty() { - warn!( "Empty backtrace with ID = {:?}", backtrace_id ); - return Ok( 0 ); + warn!("Empty backtrace with ID = {:?}", backtrace_id); + return Ok(0); } let mut i = frame_ids.len() - 1; while i > 0 { - let frame = self.data.get_frame( frame_ids[ i ] ); + let frame = self.data.get_frame(frame_ids[i]); if frame.is_inline() { i -= 1; continue; @@ -143,39 +135,44 @@ impl< 'a, T: fmt::Write > HeaptrackExporter< 'a, T > { i -= 1; - let ip_index = self.resolve_ip( frame )?; - if let Some( child ) = self.trace_tree[ parent_trace_index ].children.iter().find( |child| child.ip_index == ip_index ) { + let ip_index = self.resolve_ip(frame)?; + if let Some(child) = self.trace_tree[parent_trace_index] + .children + .iter() + .find(|child| child.ip_index == ip_index) + { parent_trace_index = child.trace_node_index; continue; } let trace_node_index = self.trace_tree.len(); - self.trace_tree.push( TraceNode { - children: Vec::new() + self.trace_tree.push(TraceNode { + children: Vec::new(), }); - self.trace_tree[ parent_trace_index ].children.push( Child { + self.trace_tree[parent_trace_index].children.push(Child { ip_index, - trace_node_index + trace_node_index, }); - writeln!( self.tx, "t {:x} {:x}", ip_index, parent_trace_index )?; + writeln!(self.tx, "t {:x} {:x}", ip_index, parent_trace_index)?; parent_trace_index = trace_node_index; } - self.backtrace_to_index.insert( backtrace_id, parent_trace_index ); - Ok( parent_trace_index ) + self.backtrace_to_index + .insert(backtrace_id, parent_trace_index); + Ok(parent_trace_index) } - fn resolve_ip( &mut self, frame: &Frame ) -> Result< usize, fmt::Error > { + fn resolve_ip(&mut self, frame: &Frame) -> Result { let address = frame.address(); - if let Some( &index ) = self.ip_to_index.get( &address ) { - return Ok( index ); + if let Some(&index) = self.ip_to_index.get(&address) { + return Ok(index); } let module_name_index; - if let Some( library_id ) = frame.library() { - module_name_index = self.resolve_string( library_id )?; + if let Some(library_id) = frame.library() { + module_name_index = self.resolve_string(library_id)?; } else { module_name_index = 0; } @@ -183,98 +180,131 @@ impl< 'a, T: fmt::Write > HeaptrackExporter< 'a, T > { let function_name_index; let source; - if let Some( id ) = frame.function().or( frame.raw_function() ) { - function_name_index = Some( self.resolve_string( id )? ); + if let Some(id) = frame.function().or(frame.raw_function()) { + function_name_index = Some(self.resolve_string(id)?); } else { function_name_index = None; }; match (frame.source(), frame.line()) { - (Some( id ), Some( line )) => { - let index = self.resolve_string( id )?; - source = Some( (index, line) ); - }, + (Some(id), Some(line)) => { + let index = self.resolve_string(id)?; + source = Some((index, line)); + } _ => { source = None; } } - write!( self.tx, "i {:x} {:x}", frame.address().raw(), module_name_index )?; - if let Some( index ) = function_name_index { - write!( self.tx, " {:x}", index )?; - - if let Some( (index, line) ) = source { - write!( self.tx, " {:x} {:x}", index, line )?; + write!( + self.tx, + "i {:x} {:x}", + frame.address().raw(), + module_name_index + )?; + if let Some(index) = function_name_index { + write!(self.tx, " {:x}", index)?; + + if let Some((index, line)) = source { + write!(self.tx, " {:x} {:x}", index, line)?; } } - writeln!( self.tx, "" )?; + writeln!(self.tx, "")?; let index = self.ip_to_index.len() + 1; - self.ip_to_index.insert( address, index ); + self.ip_to_index.insert(address, index); - Ok( index ) + Ok(index) } - fn resolve_string( &mut self, string_id: StringId ) -> Result< usize, fmt::Error > { - if let Some( &index ) = self.string_map.get( &string_id ) { - return Ok( index ); + fn resolve_string(&mut self, string_id: StringId) -> Result { + if let Some(&index) = self.string_map.get(&string_id) { + return Ok(index); } - writeln!( self.tx, "s {}", self.data.interner().resolve( string_id ).unwrap() )?; + writeln!( + self.tx, + "s {}", + self.data.interner().resolve(string_id).unwrap() + )?; let index = self.string_map.len() + 1; - self.string_map.insert( string_id, index ); - Ok( index ) + self.string_map.insert(string_id, index); + Ok(index) } - fn resolve_alloc_info( &mut self, alloc_info: AllocInfo ) -> Result< usize, fmt::Error > { - let alloc_info_index = self.alloc_info_to_index.get( &alloc_info ).cloned(); + fn resolve_alloc_info(&mut self, alloc_info: AllocInfo) -> Result { + let alloc_info_index = self.alloc_info_to_index.get(&alloc_info).cloned(); let alloc_info_index = match alloc_info_index { - Some( value ) => value, + Some(value) => value, None => { - writeln!( self.tx, "a {:x} {:x}", alloc_info.size, alloc_info.backtrace )?; + writeln!( + self.tx, + "a {:x} {:x}", + alloc_info.size, alloc_info.backtrace + )?; let index = self.alloc_info_to_index.len(); - self.alloc_info_to_index.insert( alloc_info, index ); + self.alloc_info_to_index.insert(alloc_info, index); index } }; - Ok( alloc_info_index ) + Ok(alloc_info_index) } } -fn io_err< T: fmt::Display >( err: T ) -> io::Error { - io::Error::new( io::ErrorKind::Other, format!( "serialization failed: {}", err ) ) +fn io_err(err: T) -> io::Error { + io::Error::new( + io::ErrorKind::Other, + format!("serialization failed: {}", err), + ) } -pub fn export_as_heaptrack< T: io::Write, F: Fn( AllocationId, &Allocation ) -> bool >( data: &Data, data_out: T, filter: F ) -> io::Result< () > { - let mut exporter = HeaptrackExporter::new( data, IoAdapter::new( data_out ) ).map_err( io_err )?; +pub fn export_as_heaptrack bool>( + data: &Data, + data_out: T, + filter: F, +) -> io::Result<()> { + let mut exporter = HeaptrackExporter::new(data, IoAdapter::new(data_out)).map_err(io_err)?; for op in data.operations() { match op { - Operation::Allocation { allocation, allocation_id, .. } => { - if !filter( allocation_id, allocation ) { + Operation::Allocation { + allocation, + allocation_id, + .. + } => { + if !filter(allocation_id, allocation) { continue; } - exporter.handle_alloc( allocation ).map_err( io_err )?; - }, - Operation::Deallocation { allocation, allocation_id, .. } => { - if !filter( allocation_id, allocation ) { + exporter.handle_alloc(allocation).map_err(io_err)?; + } + Operation::Deallocation { + allocation, + allocation_id, + .. + } => { + if !filter(allocation_id, allocation) { continue; } - exporter.handle_dealloc( allocation ).map_err( io_err )?; - }, - Operation::Reallocation { old_allocation, new_allocation, allocation_id, .. } => { - if filter( allocation_id, old_allocation ) { - exporter.handle_dealloc( old_allocation ).map_err( io_err )?; + exporter.handle_dealloc(allocation).map_err(io_err)?; + } + Operation::Reallocation { + old_allocation, + new_allocation, + allocation_id, + .. + } => { + if filter(allocation_id, old_allocation) { + exporter.handle_dealloc(old_allocation).map_err(io_err)?; } - if filter( allocation_id, new_allocation ) { - exporter.handle_alloc( new_allocation ).map_err( io_err )?; + if filter(allocation_id, new_allocation) { + exporter.handle_alloc(new_allocation).map_err(io_err)?; } } } diff --git a/cli-core/src/exporter_replay.rs b/cli-core/src/exporter_replay.rs index 769380c2..0f11ab53 100644 --- a/cli-core/src/exporter_replay.rs +++ b/cli-core/src/exporter_replay.rs @@ -1,5 +1,5 @@ -use std::io; use std::collections::HashMap; +use std::io; use byteorder::{NativeEndian, WriteBytesExt}; @@ -7,15 +7,15 @@ use crate::data::{Allocation, AllocationId, BacktraceId, Data, FrameId, Operatio #[derive(Default)] struct Exporter { - free_slots: Vec< usize >, + free_slots: Vec, slot_count: usize, - slot_by_pointer: HashMap< u64, usize >, - slot_by_index: Vec< usize >, - used_frames: HashMap< FrameId, u64 > + slot_by_pointer: HashMap, + slot_by_index: Vec, + used_frames: HashMap, } impl Exporter { - fn preprocess_alloc( &mut self, allocation: &Allocation ) { + fn preprocess_alloc(&mut self, allocation: &Allocation) { let slot = if self.free_slots.is_empty() { let slot = self.slot_count; self.slot_count += 1; @@ -24,34 +24,47 @@ impl Exporter { self.free_slots.pop().unwrap() }; - self.slot_by_pointer.insert( allocation.pointer, slot ); - self.slot_by_index.push( slot ); + self.slot_by_pointer.insert(allocation.pointer, slot); + self.slot_by_index.push(slot); } - fn preprocess_dealloc( &mut self, allocation: &Allocation ) { - let slot = self.slot_by_pointer.remove( &allocation.pointer ).unwrap(); - self.free_slots.push( slot ); - self.slot_by_index.push( slot ); + fn preprocess_dealloc(&mut self, allocation: &Allocation) { + let slot = self.slot_by_pointer.remove(&allocation.pointer).unwrap(); + self.free_slots.push(slot); + self.slot_by_index.push(slot); } - fn preprocess_realloc( &mut self, new_allocation: &Allocation, old_allocation: &Allocation ) { - let slot = self.slot_by_pointer.remove( &old_allocation.pointer ).unwrap(); - self.slot_by_pointer.insert( new_allocation.pointer, slot ); - self.slot_by_index.push( slot ); + fn preprocess_realloc(&mut self, new_allocation: &Allocation, old_allocation: &Allocation) { + let slot = self + .slot_by_pointer + .remove(&old_allocation.pointer) + .unwrap(); + self.slot_by_pointer.insert(new_allocation.pointer, slot); + self.slot_by_index.push(slot); } - fn preprocess_backtrace( &mut self, data: &Data, backtrace: BacktraceId ) { - for (frame_id, _) in data.get_backtrace( backtrace ) { - *self.used_frames.entry( frame_id ).or_insert( 0 ) += 1; + fn preprocess_backtrace(&mut self, data: &Data, backtrace: BacktraceId) { + for (frame_id, _) in data.get_backtrace(backtrace) { + *self.used_frames.entry(frame_id).or_insert(0) += 1; } } - fn generate_traversal( frame_map: &HashMap< FrameId, usize >, last_backtrace: &mut Option< BacktraceId >, mut output: impl io::Write, data: &Data, backtrace_id: BacktraceId ) -> io::Result< () > { - let backtrace = data.get_backtrace( backtrace_id ); - let (last_len, common_len) = if let Some( last_backtrace_id ) = *last_backtrace { - let last_backtrace = data.get_backtrace( last_backtrace_id ); + fn generate_traversal( + frame_map: &HashMap, + last_backtrace: &mut Option, + mut output: impl io::Write, + data: &Data, + backtrace_id: BacktraceId, + ) -> io::Result<()> { + let backtrace = data.get_backtrace(backtrace_id); + let (last_len, common_len) = if let Some(last_backtrace_id) = *last_backtrace { + let last_backtrace = data.get_backtrace(last_backtrace_id); let last_len = last_backtrace.len(); - let common_len = backtrace.clone().zip( last_backtrace ).take_while( |((a, _), (b, _))| a == b ).count(); + let common_len = backtrace + .clone() + .zip(last_backtrace) + .take_while(|((a, _), (b, _))| a == b) + .count(); (last_len, common_len) } else { (0, 0) @@ -59,142 +72,212 @@ impl Exporter { let go_up_count = last_len - common_len; for _ in 0..go_up_count { - output.write_u64::< NativeEndian >( 5 )?; - output.write_u64::< NativeEndian >( 0 )?; - output.write_u64::< NativeEndian >( 0 )?; - output.write_u64::< NativeEndian >( 0 )?; + output.write_u64::(5)?; + output.write_u64::(0)?; + output.write_u64::(0)?; + output.write_u64::(0)?; } - for (frame_id, _) in backtrace.skip( common_len ) { - output.write_u64::< NativeEndian >( 4 )?; - output.write_u64::< NativeEndian >( *frame_map.get( &frame_id ).unwrap() as _ )?; - output.write_u64::< NativeEndian >( 0 )?; - output.write_u64::< NativeEndian >( 0 )?; + for (frame_id, _) in backtrace.skip(common_len) { + output.write_u64::(4)?; + output.write_u64::(*frame_map.get(&frame_id).unwrap() as _)?; + output.write_u64::(0)?; + output.write_u64::(0)?; } - *last_backtrace = Some( backtrace_id ); + *last_backtrace = Some(backtrace_id); Ok(()) } - fn generate_alloc< T: io::Write >( mut output: T, slot: usize, allocation: &Allocation ) -> io::Result< () > { + fn generate_alloc( + mut output: T, + slot: usize, + allocation: &Allocation, + ) -> io::Result<()> { let timestamp = allocation.timestamp.as_usecs(); - output.write_u64::< NativeEndian >( 1 )?; - output.write_u64::< NativeEndian >( slot as u64 )?; - output.write_u64::< NativeEndian >( timestamp as u64 )?; - output.write_u64::< NativeEndian >( allocation.size as u64 )?; + output.write_u64::(1)?; + output.write_u64::(slot as u64)?; + output.write_u64::(timestamp as u64)?; + output.write_u64::(allocation.size as u64)?; Ok(()) } - fn generate_dealloc< T: io::Write >( mut output: T, slot: usize, allocation: &Allocation ) -> io::Result< () > { + fn generate_dealloc( + mut output: T, + slot: usize, + allocation: &Allocation, + ) -> io::Result<()> { let timestamp = allocation.timestamp.as_usecs(); - output.write_u64::< NativeEndian >( 2 )?; - output.write_u64::< NativeEndian >( slot as u64 )?; - output.write_u64::< NativeEndian >( timestamp as u64 )?; - output.write_u64::< NativeEndian >( 0 )?; + output.write_u64::(2)?; + output.write_u64::(slot as u64)?; + output.write_u64::(timestamp as u64)?; + output.write_u64::(0)?; Ok(()) } - fn generate_realloc< T: io::Write >( mut output: T, slot: usize, new_allocation: &Allocation ) -> io::Result< () > { + fn generate_realloc( + mut output: T, + slot: usize, + new_allocation: &Allocation, + ) -> io::Result<()> { let timestamp = new_allocation.timestamp.as_usecs(); - output.write_u64::< NativeEndian >( 3 )?; - output.write_u64::< NativeEndian >( slot as u64 )?; - output.write_u64::< NativeEndian >( timestamp as u64 )?; - output.write_u64::< NativeEndian >( new_allocation.size as u64 )?; + output.write_u64::(3)?; + output.write_u64::(slot as u64)?; + output.write_u64::(timestamp as u64)?; + output.write_u64::(new_allocation.size as u64)?; Ok(()) } - fn process< T: io::Write, F: Fn( AllocationId, &Allocation ) -> bool >( mut self, data: &Data, filter: F, mut output: T ) -> io::Result< () > { + fn process bool>( + mut self, + data: &Data, + filter: F, + mut output: T, + ) -> io::Result<()> { for operation in data.operations() { match operation { - Operation::Allocation { allocation, allocation_id, .. } => { - if !filter( allocation_id, allocation ) { + Operation::Allocation { + allocation, + allocation_id, + .. + } => { + if !filter(allocation_id, allocation) { continue; } - self.preprocess_backtrace( data, allocation.backtrace ); - self.preprocess_alloc( allocation ); - }, - Operation::Deallocation { allocation, deallocation, allocation_id, .. } => { - if !filter( allocation_id, allocation ) { + self.preprocess_backtrace(data, allocation.backtrace); + self.preprocess_alloc(allocation); + } + Operation::Deallocation { + allocation, + deallocation, + allocation_id, + .. + } => { + if !filter(allocation_id, allocation) { continue; } - if let Some( backtrace ) = deallocation.backtrace { - self.preprocess_backtrace( data, backtrace ); + if let Some(backtrace) = deallocation.backtrace { + self.preprocess_backtrace(data, backtrace); } - self.preprocess_dealloc( allocation ); - }, - Operation::Reallocation { new_allocation, old_allocation, allocation_id, .. } => { - let is_new_ok = filter( allocation_id, new_allocation ); - let is_old_ok = filter( allocation_id, old_allocation ); + self.preprocess_dealloc(allocation); + } + Operation::Reallocation { + new_allocation, + old_allocation, + allocation_id, + .. + } => { + let is_new_ok = filter(allocation_id, new_allocation); + let is_old_ok = filter(allocation_id, old_allocation); if is_new_ok || is_old_ok { - self.preprocess_backtrace( data, new_allocation.backtrace ); + self.preprocess_backtrace(data, new_allocation.backtrace); } if is_new_ok && is_old_ok { - self.preprocess_realloc( new_allocation, old_allocation ); + self.preprocess_realloc(new_allocation, old_allocation); } else if is_new_ok { - self.preprocess_alloc( new_allocation ); + self.preprocess_alloc(new_allocation); } else if is_old_ok { - self.preprocess_dealloc( old_allocation ); + self.preprocess_dealloc(old_allocation); } } } } - output.write_u64::< NativeEndian >( self.slot_count as u64 )?; - let mut frames: Vec< _ > = self.used_frames.drain().collect(); - frames.sort_by_key( |&(_, count)| count ); + output.write_u64::(self.slot_count as u64)?; + let mut frames: Vec<_> = self.used_frames.drain().collect(); + frames.sort_by_key(|&(_, count)| count); frames.reverse(); - let frame_map: HashMap< _, _ > = - frames.into_iter().enumerate().map( |(index, (frame_id, _))| (frame_id, index) ).collect(); + let frame_map: HashMap<_, _> = frames + .into_iter() + .enumerate() + .map(|(index, (frame_id, _))| (frame_id, index)) + .collect(); let mut last_backtrace = None; - for (operation, slot) in data.operations().zip( self.slot_by_index ) { + for (operation, slot) in data.operations().zip(self.slot_by_index) { match operation { - Operation::Allocation { allocation, allocation_id, .. } => { - if !filter( allocation_id, allocation ) { + Operation::Allocation { + allocation, + allocation_id, + .. + } => { + if !filter(allocation_id, allocation) { continue; } - Self::generate_traversal( &frame_map, &mut last_backtrace, &mut output, data, allocation.backtrace )?; - Self::generate_alloc( &mut output, slot, allocation )?; - }, - Operation::Deallocation { allocation, deallocation, allocation_id, .. } => { - if !filter( allocation_id, allocation ) { + Self::generate_traversal( + &frame_map, + &mut last_backtrace, + &mut output, + data, + allocation.backtrace, + )?; + Self::generate_alloc(&mut output, slot, allocation)?; + } + Operation::Deallocation { + allocation, + deallocation, + allocation_id, + .. + } => { + if !filter(allocation_id, allocation) { continue; } - if let Some( backtrace ) = deallocation.backtrace { - Self::generate_traversal( &frame_map, &mut last_backtrace, &mut output, data, backtrace )?; + if let Some(backtrace) = deallocation.backtrace { + Self::generate_traversal( + &frame_map, + &mut last_backtrace, + &mut output, + data, + backtrace, + )?; } - Self::generate_dealloc( &mut output, slot, allocation )?; - }, - Operation::Reallocation { new_allocation, old_allocation, allocation_id, .. } => { - let is_new_ok = filter( allocation_id, new_allocation ); - let is_old_ok = filter( allocation_id, old_allocation ); + Self::generate_dealloc(&mut output, slot, allocation)?; + } + Operation::Reallocation { + new_allocation, + old_allocation, + allocation_id, + .. + } => { + let is_new_ok = filter(allocation_id, new_allocation); + let is_old_ok = filter(allocation_id, old_allocation); if is_new_ok || is_old_ok { - Self::generate_traversal( &frame_map, &mut last_backtrace, &mut output, data, new_allocation.backtrace )?; + Self::generate_traversal( + &frame_map, + &mut last_backtrace, + &mut output, + data, + new_allocation.backtrace, + )?; } if is_new_ok && is_old_ok { - Self::generate_realloc( &mut output, slot, new_allocation )?; + Self::generate_realloc(&mut output, slot, new_allocation)?; } else if is_new_ok { - Self::generate_alloc( &mut output, slot, new_allocation )?; + Self::generate_alloc(&mut output, slot, new_allocation)?; } else if is_old_ok { - Self::generate_dealloc( &mut output, slot, old_allocation )?; + Self::generate_dealloc(&mut output, slot, old_allocation)?; } } } } - output.write_u64::< NativeEndian >( 0 )?; + output.write_u64::(0)?; Ok(()) } } -pub fn export_as_replay< T: io::Write, F: Fn( AllocationId, &Allocation ) -> bool >( data: &Data, output: T, filter: F ) -> io::Result< () > { - Exporter::default().process( data, filter, output ) +pub fn export_as_replay bool>( + data: &Data, + output: T, + filter: F, +) -> io::Result<()> { + Exporter::default().process(data, filter, output) } diff --git a/cli-core/src/filter.rs b/cli-core/src/filter.rs index fda15919..e8b08d8c 100644 --- a/cli-core/src/filter.rs +++ b/cli-core/src/filter.rs @@ -1,67 +1,67 @@ -use regex::Regex; +use crate::{Allocation, BacktraceId, Data, DataPointer, Map, MapId, Timestamp}; use ahash::AHashMap as HashMap; use ahash::AHashSet as HashSet; -use crate::{Allocation, BacktraceId, Data, Timestamp, DataPointer, Map, MapId}; +use regex::Regex; pub trait TryMatch { type Item; - fn try_match( &self, data: &Data, item: &Self::Item ) -> bool; + fn try_match(&self, data: &Data, item: &Self::Item) -> bool; } pub trait Compile { type Compiled: TryMatch + Send + Sync; - fn compile( &self, data: &Data ) -> Self::Compiled; + fn compile(&self, data: &Data) -> Self::Compiled; } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] -pub struct Duration( pub common::Timestamp ); +pub struct Duration(pub common::Timestamp); impl Duration { - pub fn from_secs( value: u64 ) -> Self { - Self( common::Timestamp::from_secs( value ) ) + pub fn from_secs(value: u64) -> Self { + Self(common::Timestamp::from_secs(value)) } - pub fn from_usecs( value: u64 ) -> Self { - Self( common::Timestamp::from_usecs( value ) ) + pub fn from_usecs(value: u64) -> Self { + Self(common::Timestamp::from_usecs(value)) } - pub fn from_msecs( value: u64 ) -> Self { - Self( common::Timestamp::from_msecs( value ) ) + pub fn from_msecs(value: u64) -> Self { + Self(common::Timestamp::from_msecs(value)) } } #[derive(Clone, Default)] pub struct RawBacktraceFilter { - pub only_passing_through_function: Option< Regex >, - pub only_not_passing_through_function: Option< Regex >, - pub only_passing_through_source: Option< Regex >, - pub only_not_passing_through_source: Option< Regex >, - pub only_matching_backtraces: Option< HashSet< BacktraceId > >, - pub only_not_matching_backtraces: Option< HashSet< BacktraceId > >, - pub only_backtrace_length_at_least: Option< usize >, - pub only_backtrace_length_at_most: Option< usize >, - - pub only_matching_deallocation_backtraces: Option< HashSet< BacktraceId > >, - pub only_not_matching_deallocation_backtraces: Option< HashSet< BacktraceId > >, + pub only_passing_through_function: Option, + pub only_not_passing_through_function: Option, + pub only_passing_through_source: Option, + pub only_not_passing_through_source: Option, + pub only_matching_backtraces: Option>, + pub only_not_matching_backtraces: Option>, + pub only_backtrace_length_at_least: Option, + pub only_backtrace_length_at_most: Option, + + pub only_matching_deallocation_backtraces: Option>, + pub only_not_matching_deallocation_backtraces: Option>, } #[derive(Clone, Default)] pub struct RawCommonFilter { - pub only_larger_or_equal: Option< u64 >, - pub only_larger: Option< u64 >, - pub only_smaller_or_equal: Option< u64 >, - pub only_smaller: Option< u64 >, - - pub only_address_at_least: Option< u64 >, - pub only_address_at_most: Option< u64 >, - pub only_allocated_after_at_least: Option< Duration >, - pub only_allocated_until_at_most: Option< Duration >, - pub only_deallocated_after_at_least: Option< Duration >, - pub only_deallocated_until_at_most: Option< Duration >, - pub only_alive_at: Vec< Duration >, - pub only_alive_for_at_least: Option< Duration >, - pub only_alive_for_at_most: Option< Duration >, - pub only_leaked_or_deallocated_after: Option< Duration >, + pub only_larger_or_equal: Option, + pub only_larger: Option, + pub only_smaller_or_equal: Option, + pub only_smaller: Option, + + pub only_address_at_least: Option, + pub only_address_at_most: Option, + pub only_allocated_after_at_least: Option, + pub only_allocated_until_at_most: Option, + pub only_deallocated_after_at_least: Option, + pub only_deallocated_until_at_most: Option, + pub only_alive_at: Vec, + pub only_alive_for_at_least: Option, + pub only_alive_for_at_most: Option, + pub only_leaked_or_deallocated_after: Option, pub only_leaked: bool, pub only_temporary: bool, @@ -72,29 +72,29 @@ pub struct RawAllocationFilter { pub backtrace_filter: RawBacktraceFilter, pub common_filter: RawCommonFilter, - pub only_first_size_larger_or_equal: Option< u64 >, - pub only_first_size_larger: Option< u64 >, - pub only_first_size_smaller_or_equal: Option< u64 >, - pub only_first_size_smaller: Option< u64 >, - pub only_last_size_larger_or_equal: Option< u64 >, - pub only_last_size_larger: Option< u64 >, - pub only_last_size_smaller_or_equal: Option< u64 >, - pub only_last_size_smaller: Option< u64 >, - pub only_chain_length_at_least: Option< u32 >, - pub only_chain_length_at_most: Option< u32 >, - pub only_chain_alive_for_at_least: Option< Duration >, - pub only_chain_alive_for_at_most: Option< Duration >, - pub only_position_in_chain_at_least: Option< u32 >, - pub only_position_in_chain_at_most: Option< u32 >, - - pub only_group_allocations_at_least: Option< usize >, - pub only_group_allocations_at_most: Option< usize >, - pub only_group_interval_at_least: Option< Duration >, - pub only_group_interval_at_most: Option< Duration >, - pub only_group_max_total_usage_first_seen_at_least: Option< Duration >, - pub only_group_max_total_usage_first_seen_at_most: Option< Duration >, - pub only_group_leaked_allocations_at_least: Option< NumberOrFractionOfTotal >, - pub only_group_leaked_allocations_at_most: Option< NumberOrFractionOfTotal >, + pub only_first_size_larger_or_equal: Option, + pub only_first_size_larger: Option, + pub only_first_size_smaller_or_equal: Option, + pub only_first_size_smaller: Option, + pub only_last_size_larger_or_equal: Option, + pub only_last_size_larger: Option, + pub only_last_size_smaller_or_equal: Option, + pub only_last_size_smaller: Option, + pub only_chain_length_at_least: Option, + pub only_chain_length_at_most: Option, + pub only_chain_alive_for_at_least: Option, + pub only_chain_alive_for_at_most: Option, + pub only_position_in_chain_at_least: Option, + pub only_position_in_chain_at_most: Option, + + pub only_group_allocations_at_least: Option, + pub only_group_allocations_at_most: Option, + pub only_group_interval_at_least: Option, + pub only_group_interval_at_most: Option, + pub only_group_max_total_usage_first_seen_at_least: Option, + pub only_group_max_total_usage_first_seen_at_most: Option, + pub only_group_leaked_allocations_at_least: Option, + pub only_group_leaked_allocations_at_most: Option, pub only_chain_leaked: bool, pub only_ptmalloc_mmaped: bool, @@ -103,9 +103,9 @@ pub struct RawAllocationFilter { pub only_ptmalloc_not_from_main_arena: bool, pub only_jemalloc: bool, pub only_not_jemalloc: bool, - pub only_with_marker: Option< u32 >, + pub only_with_marker: Option, - pub only_from_maps: Option< HashSet< MapId > >, + pub only_from_maps: Option>, } #[derive(Clone, Default)] @@ -113,8 +113,8 @@ pub struct RawMapFilter { pub backtrace_filter: RawBacktraceFilter, pub common_filter: RawCommonFilter, - pub only_peak_rss_at_least: Option< u64 >, - pub only_peak_rss_at_most: Option< u64 >, + pub only_peak_rss_at_least: Option, + pub only_peak_rss_at_most: Option, pub only_jemalloc: bool, pub only_not_jemalloc: bool, pub only_bytehound: bool, @@ -129,26 +129,26 @@ pub struct RawMapFilter { #[derive(Copy, Clone)] pub enum NumberOrFractionOfTotal { - Number( u64 ), - Fraction( f64 ) + Number(u64), + Fraction(f64), } impl NumberOrFractionOfTotal { - pub fn get( self, total: u64 ) -> u64 { + pub fn get(self, total: u64) -> u64 { match self { - NumberOrFractionOfTotal::Number( value ) => value, - NumberOrFractionOfTotal::Fraction( fraction ) => (total as f64 * fraction) as u64 + NumberOrFractionOfTotal::Number(value) => value, + NumberOrFractionOfTotal::Fraction(fraction) => (total as f64 * fraction) as u64, } } } #[derive(Clone)] pub struct RawCompiledBacktraceFilter { - only_backtraces: Option< HashSet< BacktraceId > >, - only_not_matching_backtraces: Option< HashSet< BacktraceId > >, + only_backtraces: Option>, + only_not_matching_backtraces: Option>, - only_deallocation_backtraces: Option< HashSet< BacktraceId > >, - only_not_matching_deallocation_backtraces: Option< HashSet< BacktraceId > >, + only_deallocation_backtraces: Option>, + only_not_matching_deallocation_backtraces: Option>, } #[derive(Clone)] @@ -161,9 +161,9 @@ pub struct RawCompiledCommonFilter { only_address_at_most: u64, only_allocated_after_at_least: Timestamp, only_allocated_until_at_most: Timestamp, - only_deallocated_between_inclusive: Option< (Timestamp, Timestamp) >, + only_deallocated_between_inclusive: Option<(Timestamp, Timestamp)>, only_alive_for_at_least: Duration, - only_alive_for_at_most: Option< Duration >, + only_alive_for_at_most: Option, only_leaked_or_deallocated_after: Timestamp, } @@ -182,9 +182,9 @@ pub struct RawCompiledAllocationFilter { only_chain_length_at_least: u32, only_chain_length_at_most: u32, only_chain_alive_for_at_least: Duration, - only_chain_alive_for_at_most: Option< Duration >, + only_chain_alive_for_at_most: Option, only_chain_leaked_or_deallocated_after: Timestamp, - only_chain_deallocated_between_inclusive: Option< (Timestamp, Timestamp) >, + only_chain_deallocated_between_inclusive: Option<(Timestamp, Timestamp)>, only_position_in_chain_at_least: u32, only_position_in_chain_at_most: u32, @@ -198,12 +198,12 @@ pub struct RawCompiledAllocationFilter { only_group_leaked_allocations_at_least: NumberOrFractionOfTotal, only_group_leaked_allocations_at_most: NumberOrFractionOfTotal, - only_ptmalloc_mmaped: Option< bool >, - only_ptmalloc_from_main_arena: Option< bool >, - only_jemalloc: Option< bool >, - only_with_marker: Option< u32 >, + only_ptmalloc_mmaped: Option, + only_ptmalloc_from_main_arena: Option, + only_jemalloc: Option, + only_with_marker: Option, - only_from_maps: Option< Vec< MapId > > + only_from_maps: Option>, } #[derive(Clone)] @@ -213,91 +213,101 @@ pub struct RawCompiledMapFilter { backtrace_filter: RawCompiledBacktraceFilter, common_filter: RawCompiledCommonFilter, - only_peak_rss_at_least: Option< u64 >, - only_peak_rss_at_most: Option< u64 >, - jemalloc_filter: Option< bool >, - bytehound_filter: Option< bool >, - readable_filter: Option< bool >, - writable_filter: Option< bool >, - executable_filter: Option< bool >, + only_peak_rss_at_least: Option, + only_peak_rss_at_most: Option, + jemalloc_filter: Option, + bytehound_filter: Option, + readable_filter: Option, + writable_filter: Option, + executable_filter: Option, } -impl< T > From< T > for Filter< T > { - fn from( filter: T ) -> Self { - Filter::Basic( filter ) +impl From for Filter { + fn from(filter: T) -> Self { + Filter::Basic(filter) } } #[derive(Clone)] -pub enum Filter< T > { - Basic( T ), - And( Box< Filter< T > >, Box< Filter< T > > ), - Or( Box< Filter< T > >, Box< Filter< T > > ), - Not( Box< Filter< T > > ), +pub enum Filter { + Basic(T), + And(Box>, Box>), + Or(Box>, Box>), + Not(Box>), } -fn compile_backtrace_filter( data: &Data, filter: &RawBacktraceFilter ) -> Option< HashSet< BacktraceId > > { - let is_none = - filter.only_passing_through_function.is_none() && - filter.only_not_passing_through_function.is_none() && - filter.only_passing_through_source.is_none() && - filter.only_not_passing_through_source.is_none() && - filter.only_backtrace_length_at_least.is_none() && - filter.only_backtrace_length_at_most.is_none(); +fn compile_backtrace_filter( + data: &Data, + filter: &RawBacktraceFilter, +) -> Option> { + let is_none = filter.only_passing_through_function.is_none() + && filter.only_not_passing_through_function.is_none() + && filter.only_passing_through_source.is_none() + && filter.only_not_passing_through_source.is_none() + && filter.only_backtrace_length_at_least.is_none() + && filter.only_backtrace_length_at_most.is_none(); if is_none { return filter.only_matching_backtraces.clone(); } - let only_backtrace_length_at_least = filter.only_backtrace_length_at_least.unwrap_or( 0 ); - let only_backtrace_length_at_most = filter.only_backtrace_length_at_most.unwrap_or( !0 ); + let only_backtrace_length_at_least = filter.only_backtrace_length_at_least.unwrap_or(0); + let only_backtrace_length_at_most = filter.only_backtrace_length_at_most.unwrap_or(!0); let mut matched_backtraces = HashSet::new(); let mut positive_cache = HashMap::new(); let mut negative_cache = HashMap::new(); for (backtrace_id, backtrace) in data.all_backtraces() { - if backtrace.len() < only_backtrace_length_at_least || backtrace.len() > only_backtrace_length_at_most { + if backtrace.len() < only_backtrace_length_at_least + || backtrace.len() > only_backtrace_length_at_most + { continue; } - let mut positive_matched = - filter.only_passing_through_function.is_none() && - filter.only_passing_through_source.is_none(); + let mut positive_matched = filter.only_passing_through_function.is_none() + && filter.only_passing_through_source.is_none(); let mut negative_matched = false; - let check_negative = - filter.only_not_passing_through_function.is_some() || - filter.only_not_passing_through_source.is_some(); + let check_negative = filter.only_not_passing_through_function.is_some() + || filter.only_not_passing_through_source.is_some(); for (frame_id, frame) in backtrace { - let check_positive = - if positive_matched { - false - } else if let Some( &cached_result ) = positive_cache.get( &frame_id ) { - positive_matched = cached_result; - false - } else { - true - }; + let check_positive = if positive_matched { + false + } else if let Some(&cached_result) = positive_cache.get(&frame_id) { + positive_matched = cached_result; + false + } else { + true + }; if positive_matched && !check_negative { break; } let mut function = None; - if (check_positive && filter.only_passing_through_function.is_some()) || filter.only_not_passing_through_function.is_some() { - function = frame.function().or_else( || frame.raw_function() ).map( |id| data.interner().resolve( id ).unwrap() ); + if (check_positive && filter.only_passing_through_function.is_some()) + || filter.only_not_passing_through_function.is_some() + { + function = frame + .function() + .or_else(|| frame.raw_function()) + .map(|id| data.interner().resolve(id).unwrap()); } let mut source = None; - if (check_positive && filter.only_passing_through_source.is_some()) || filter.only_not_passing_through_source.is_some() { - source = frame.source().map( |id| data.interner().resolve( id ).unwrap() ) + if (check_positive && filter.only_passing_through_source.is_some()) + || filter.only_not_passing_through_source.is_some() + { + source = frame + .source() + .map(|id| data.interner().resolve(id).unwrap()) } if check_positive { let matched_function = - if let Some( regex ) = filter.only_passing_through_function.as_ref() { - if let Some( ref function ) = function { - regex.is_match( function ) + if let Some(regex) = filter.only_passing_through_function.as_ref() { + if let Some(ref function) = function { + regex.is_match(function) } else { false } @@ -306,9 +316,9 @@ fn compile_backtrace_filter( data: &Data, filter: &RawBacktraceFilter ) -> Optio }; let matched_source = - if let Some( regex ) = filter.only_passing_through_source.as_ref() { - if let Some( ref source ) = source { - regex.is_match( source ) + if let Some(regex) = filter.only_passing_through_source.as_ref() { + if let Some(ref source) = source { + regex.is_match(source) } else { false } @@ -317,89 +327,101 @@ fn compile_backtrace_filter( data: &Data, filter: &RawBacktraceFilter ) -> Optio }; positive_matched = matched_function && matched_source; - positive_cache.insert( frame_id, positive_matched ); + positive_cache.insert(frame_id, positive_matched); } if check_negative { - match negative_cache.get( &frame_id ).cloned() { - Some( true ) => { + match negative_cache.get(&frame_id).cloned() { + Some(true) => { negative_matched = true; break; - }, - Some( false ) => { + } + Some(false) => { continue; - }, + } None => {} } - if let Some( regex ) = filter.only_not_passing_through_function.as_ref() { - if let Some( ref function ) = function { - if regex.is_match( function ) { - negative_cache.insert( frame_id, true ); + if let Some(regex) = filter.only_not_passing_through_function.as_ref() { + if let Some(ref function) = function { + if regex.is_match(function) { + negative_cache.insert(frame_id, true); negative_matched = true; break; } } } - if let Some( regex ) = filter.only_not_passing_through_source.as_ref() { - if let Some( ref source ) = source { - if regex.is_match( source ) { - negative_cache.insert( frame_id, true ); + if let Some(regex) = filter.only_not_passing_through_source.as_ref() { + if let Some(ref source) = source { + if regex.is_match(source) { + negative_cache.insert(frame_id, true); negative_matched = true; break; } } } - negative_cache.insert( frame_id, false ); + negative_cache.insert(frame_id, false); } } if positive_matched && !negative_matched { - matched_backtraces.insert( backtrace_id ); + matched_backtraces.insert(backtrace_id); } } - if let Some( ref only_matching_backtraces ) = filter.only_matching_backtraces { - matched_backtraces = matched_backtraces.intersection( &only_matching_backtraces ).copied().collect(); + if let Some(ref only_matching_backtraces) = filter.only_matching_backtraces { + matched_backtraces = matched_backtraces + .intersection(&only_matching_backtraces) + .copied() + .collect(); } - Some( matched_backtraces ) + Some(matched_backtraces) } impl Compile for RawCommonFilter { type Compiled = RawCompiledCommonFilter; - fn compile( &self, data: &Data ) -> Self::Compiled { + fn compile(&self, data: &Data) -> Self::Compiled { let mut is_impossible = false; - let mut only_larger_or_equal = self.only_larger_or_equal.unwrap_or( 0 ); - if let Some( only_larger ) = self.only_larger { + let mut only_larger_or_equal = self.only_larger_or_equal.unwrap_or(0); + if let Some(only_larger) = self.only_larger { if only_larger == !0 { is_impossible = true; } else { - only_larger_or_equal = std::cmp::max( only_larger_or_equal, only_larger + 1 ); + only_larger_or_equal = std::cmp::max(only_larger_or_equal, only_larger + 1); } } - let mut only_smaller_or_equal = self.only_smaller_or_equal.unwrap_or( !0 ); - if let Some( only_smaller ) = self.only_smaller { + let mut only_smaller_or_equal = self.only_smaller_or_equal.unwrap_or(!0); + if let Some(only_smaller) = self.only_smaller { if only_smaller == 0 { is_impossible = true; } else { - only_smaller_or_equal = std::cmp::min( only_smaller_or_equal, only_smaller - 1 ); + only_smaller_or_equal = std::cmp::min(only_smaller_or_equal, only_smaller - 1); } } let mut only_deallocated_between_inclusive = None; - if self.only_deallocated_after_at_least.is_some() || self.only_deallocated_until_at_most.is_some() { + if self.only_deallocated_after_at_least.is_some() + || self.only_deallocated_until_at_most.is_some() + { only_deallocated_between_inclusive = Some(( - self.only_deallocated_after_at_least.map( |offset| data.initial_timestamp + offset.0 ).unwrap_or( data.initial_timestamp ), - self.only_deallocated_until_at_most.map( |offset| data.initial_timestamp + offset.0 ).unwrap_or( data.last_timestamp ) + self.only_deallocated_after_at_least + .map(|offset| data.initial_timestamp + offset.0) + .unwrap_or(data.initial_timestamp), + self.only_deallocated_until_at_most + .map(|offset| data.initial_timestamp + offset.0) + .unwrap_or(data.last_timestamp), )); } - let mut only_leaked_or_deallocated_after = self.only_leaked_or_deallocated_after.map( |offset| data.initial_timestamp + offset.0 ).unwrap_or( data.initial_timestamp ); + let mut only_leaked_or_deallocated_after = self + .only_leaked_or_deallocated_after + .map(|offset| data.initial_timestamp + offset.0) + .unwrap_or(data.initial_timestamp); if self.only_leaked && self.only_temporary { is_impossible = true; @@ -410,33 +432,44 @@ impl Compile for RawCommonFilter { } if self.only_temporary { - if let Some( (ref mut min, ref mut max) ) = only_deallocated_between_inclusive { - *min = std::cmp::max( *min, data.initial_timestamp ); - *max = std::cmp::min( *max, data.last_timestamp ); + if let Some((ref mut min, ref mut max)) = only_deallocated_between_inclusive { + *min = std::cmp::max(*min, data.initial_timestamp); + *max = std::cmp::min(*max, data.last_timestamp); } else { - only_deallocated_between_inclusive = Some( (data.initial_timestamp, data.last_timestamp) ); + only_deallocated_between_inclusive = + Some((data.initial_timestamp, data.last_timestamp)); } } - let only_allocated_after_at_least = self.only_allocated_after_at_least.map( |offset| data.initial_timestamp + offset.0 ).unwrap_or( data.initial_timestamp ); - let mut only_allocated_until_at_most = self.only_allocated_until_at_most.map( |offset| data.initial_timestamp + offset.0 ).unwrap_or( data.last_timestamp ); + let only_allocated_after_at_least = self + .only_allocated_after_at_least + .map(|offset| data.initial_timestamp + offset.0) + .unwrap_or(data.initial_timestamp); + let mut only_allocated_until_at_most = self + .only_allocated_until_at_most + .map(|offset| data.initial_timestamp + offset.0) + .unwrap_or(data.last_timestamp); for only_alive_at in &self.only_alive_at { let only_alive_at = only_alive_at.0 + data.initial_timestamp; - only_allocated_until_at_most = std::cmp::min( only_allocated_until_at_most, only_alive_at ); - only_leaked_or_deallocated_after = std::cmp::max( only_leaked_or_deallocated_after, only_alive_at ); + only_allocated_until_at_most = + std::cmp::min(only_allocated_until_at_most, only_alive_at); + only_leaked_or_deallocated_after = + std::cmp::max(only_leaked_or_deallocated_after, only_alive_at); } Self::Compiled { is_impossible, only_larger_or_equal, only_smaller_or_equal, - only_address_at_least: self.only_address_at_least.unwrap_or( 0 ), - only_address_at_most: self.only_address_at_most.unwrap_or( !0 ), + only_address_at_least: self.only_address_at_least.unwrap_or(0), + only_address_at_most: self.only_address_at_most.unwrap_or(!0), only_allocated_after_at_least, only_allocated_until_at_most, only_deallocated_between_inclusive: only_deallocated_between_inclusive, - only_alive_for_at_least: self.only_alive_for_at_least.unwrap_or( Duration::from_secs( 0 ) ), + only_alive_for_at_least: self + .only_alive_for_at_least + .unwrap_or(Duration::from_secs(0)), only_alive_for_at_most: self.only_alive_for_at_most, only_leaked_or_deallocated_after, } @@ -445,45 +478,53 @@ impl Compile for RawCommonFilter { impl Compile for RawAllocationFilter { type Compiled = RawCompiledAllocationFilter; - fn compile( &self, data: &Data ) -> Self::Compiled { + fn compile(&self, data: &Data) -> Self::Compiled { let mut is_impossible = false; - let only_backtraces = compile_backtrace_filter( data, &self.backtrace_filter ); - let mut common_filter = self.common_filter.compile( data ); + let only_backtraces = compile_backtrace_filter(data, &self.backtrace_filter); + let mut common_filter = self.common_filter.compile(data); is_impossible = is_impossible || common_filter.is_impossible; - let mut only_first_size_larger_or_equal = self.only_first_size_larger_or_equal.unwrap_or( 0 ); - if let Some( only_first_size_larger ) = self.only_first_size_larger { + let mut only_first_size_larger_or_equal = self.only_first_size_larger_or_equal.unwrap_or(0); + if let Some(only_first_size_larger) = self.only_first_size_larger { if only_first_size_larger == !0 { is_impossible = true; } else { - only_first_size_larger_or_equal = std::cmp::max( only_first_size_larger_or_equal, only_first_size_larger + 1 ); + only_first_size_larger_or_equal = + std::cmp::max(only_first_size_larger_or_equal, only_first_size_larger + 1); } } - let mut only_first_size_smaller_or_equal = self.only_first_size_smaller_or_equal.unwrap_or( !0 ); - if let Some( only_first_size_smaller ) = self.only_first_size_smaller { + let mut only_first_size_smaller_or_equal = + self.only_first_size_smaller_or_equal.unwrap_or(!0); + if let Some(only_first_size_smaller) = self.only_first_size_smaller { if only_first_size_smaller == 0 { is_impossible = true; } else { - only_first_size_smaller_or_equal = std::cmp::min( only_first_size_smaller_or_equal, only_first_size_smaller - 1 ); + only_first_size_smaller_or_equal = std::cmp::min( + only_first_size_smaller_or_equal, + only_first_size_smaller - 1, + ); } } - let mut only_last_size_larger_or_equal = self.only_last_size_larger_or_equal.unwrap_or( 0 ); - if let Some( only_last_size_larger ) = self.only_last_size_larger { + let mut only_last_size_larger_or_equal = self.only_last_size_larger_or_equal.unwrap_or(0); + if let Some(only_last_size_larger) = self.only_last_size_larger { if only_last_size_larger == !0 { is_impossible = true; } else { - only_last_size_larger_or_equal = std::cmp::max( only_last_size_larger_or_equal, only_last_size_larger + 1 ); + only_last_size_larger_or_equal = + std::cmp::max(only_last_size_larger_or_equal, only_last_size_larger + 1); } } - let mut only_last_size_smaller_or_equal = self.only_last_size_smaller_or_equal.unwrap_or( !0 ); - if let Some( only_last_size_smaller ) = self.only_last_size_smaller { + let mut only_last_size_smaller_or_equal = + self.only_last_size_smaller_or_equal.unwrap_or(!0); + if let Some(only_last_size_smaller) = self.only_last_size_smaller { if only_last_size_smaller == 0 { is_impossible = true; } else { - only_last_size_smaller_or_equal = std::cmp::min( only_last_size_smaller_or_equal, only_last_size_smaller - 1 ); + only_last_size_smaller_or_equal = + std::cmp::min(only_last_size_smaller_or_equal, only_last_size_smaller - 1); } } @@ -499,53 +540,68 @@ impl Compile for RawAllocationFilter { is_impossible = true; } - if let Some( ref map_ids ) = self.only_from_maps { + if let Some(ref map_ids) = self.only_from_maps { if map_ids.is_empty() { is_impossible = true; } else { - let mut iter = map_ids.iter().copied().map( |map_id| { - let map = &data.maps[ map_id.raw() as usize ]; + let mut iter = map_ids.iter().copied().map(|map_id| { + let map = &data.maps[map_id.raw() as usize]; ( map.pointer, map.pointer + map.size, - map.source.as_ref().map( |source| source.timestamp ).unwrap_or( map.timestamp ), - map.deallocation.as_ref().map( |deallocation| { - deallocation.source.as_ref().map( |source| source.timestamp ).unwrap_or( deallocation.timestamp ) - }).unwrap_or( data.last_timestamp ) + map.source + .as_ref() + .map(|source| source.timestamp) + .unwrap_or(map.timestamp), + map.deallocation + .as_ref() + .map(|deallocation| { + deallocation + .source + .as_ref() + .map(|source| source.timestamp) + .unwrap_or(deallocation.timestamp) + }) + .unwrap_or(data.last_timestamp), ) }); - let (mut address_min, mut address_max, mut timestamp_min, mut timestamp_max) = iter.next().unwrap(); - for (new_address_min, new_address_max, new_timestamp_min, new_timestamp_max) in iter { - address_min = std::cmp::min( address_min, new_address_min ); - address_max = std::cmp::max( address_max, new_address_max ); - timestamp_min = std::cmp::min( timestamp_min, new_timestamp_min ); - timestamp_max = std::cmp::max( timestamp_max, new_timestamp_max ); + let (mut address_min, mut address_max, mut timestamp_min, mut timestamp_max) = + iter.next().unwrap(); + for (new_address_min, new_address_max, new_timestamp_min, new_timestamp_max) in iter + { + address_min = std::cmp::min(address_min, new_address_min); + address_max = std::cmp::max(address_max, new_address_max); + timestamp_min = std::cmp::min(timestamp_min, new_timestamp_min); + timestamp_max = std::cmp::max(timestamp_max, new_timestamp_max); } - common_filter.only_address_at_least = std::cmp::max( common_filter.only_address_at_least, address_min ); - common_filter.only_address_at_most = std::cmp::min( common_filter.only_address_at_most, address_max ); - common_filter.only_allocated_after_at_least = std::cmp::max( common_filter.only_allocated_after_at_least, timestamp_min ); - common_filter.only_allocated_until_at_most = std::cmp::min( common_filter.only_allocated_until_at_most, timestamp_max ); + common_filter.only_address_at_least = + std::cmp::max(common_filter.only_address_at_least, address_min); + common_filter.only_address_at_most = + std::cmp::min(common_filter.only_address_at_most, address_max); + common_filter.only_allocated_after_at_least = + std::cmp::max(common_filter.only_allocated_after_at_least, timestamp_min); + common_filter.only_allocated_until_at_most = + std::cmp::min(common_filter.only_allocated_until_at_most, timestamp_max); } } - let enable_chain_filter = - self.only_first_size_larger.is_some() || - self.only_first_size_larger_or_equal.is_some() || - self.only_first_size_smaller.is_some() || - self.only_first_size_smaller_or_equal.is_some() || - self.only_last_size_larger.is_some() || - self.only_last_size_larger_or_equal.is_some() || - self.only_last_size_smaller.is_some() || - self.only_last_size_smaller_or_equal.is_some() || - self.only_chain_length_at_least.is_some() || - self.only_chain_length_at_most.is_some() || - self.only_chain_alive_for_at_least.is_some() || - self.only_chain_alive_for_at_most.is_some() || - self.only_position_in_chain_at_least.is_some() || - self.only_position_in_chain_at_most.is_some() || - self.only_chain_leaked; + let enable_chain_filter = self.only_first_size_larger.is_some() + || self.only_first_size_larger_or_equal.is_some() + || self.only_first_size_smaller.is_some() + || self.only_first_size_smaller_or_equal.is_some() + || self.only_last_size_larger.is_some() + || self.only_last_size_larger_or_equal.is_some() + || self.only_last_size_smaller.is_some() + || self.only_last_size_smaller_or_equal.is_some() + || self.only_chain_length_at_least.is_some() + || self.only_chain_length_at_most.is_some() + || self.only_chain_alive_for_at_least.is_some() + || self.only_chain_alive_for_at_most.is_some() + || self.only_position_in_chain_at_least.is_some() + || self.only_position_in_chain_at_most.is_some() + || self.only_chain_leaked; let mut only_chain_leaked_or_deallocated_after = data.initial_timestamp; if self.only_chain_leaked { @@ -554,25 +610,35 @@ impl Compile for RawAllocationFilter { let only_chain_deallocated_between_inclusive = None; - let enable_group_filter = - self.only_group_allocations_at_least.is_some() || - self.only_group_allocations_at_most.is_some() || - self.only_group_interval_at_least.is_some() || - self.only_group_interval_at_most.is_some() || - self.only_group_max_total_usage_first_seen_at_least.is_some() || - self.only_group_max_total_usage_first_seen_at_most.is_some() || - self.only_group_leaked_allocations_at_least.is_some() || - self.only_group_leaked_allocations_at_most.is_some(); + let enable_group_filter = self.only_group_allocations_at_least.is_some() + || self.only_group_allocations_at_most.is_some() + || self.only_group_interval_at_least.is_some() + || self.only_group_interval_at_most.is_some() + || self + .only_group_max_total_usage_first_seen_at_least + .is_some() + || self.only_group_max_total_usage_first_seen_at_most.is_some() + || self.only_group_leaked_allocations_at_least.is_some() + || self.only_group_leaked_allocations_at_most.is_some(); RawCompiledAllocationFilter { is_impossible, backtrace_filter: RawCompiledBacktraceFilter { only_backtraces, - only_not_matching_backtraces: self.backtrace_filter.only_not_matching_backtraces.clone(), - - only_deallocation_backtraces: self.backtrace_filter.only_matching_deallocation_backtraces.clone(), - only_not_matching_deallocation_backtraces: self.backtrace_filter.only_not_matching_deallocation_backtraces.clone(), + only_not_matching_backtraces: self + .backtrace_filter + .only_not_matching_backtraces + .clone(), + + only_deallocation_backtraces: self + .backtrace_filter + .only_matching_deallocation_backtraces + .clone(), + only_not_matching_deallocation_backtraces: self + .backtrace_filter + .only_not_matching_deallocation_backtraces + .clone(), }, common_filter, @@ -582,76 +648,91 @@ impl Compile for RawAllocationFilter { only_first_size_smaller_or_equal, only_last_size_larger_or_equal, only_last_size_smaller_or_equal, - only_chain_length_at_least: self.only_chain_length_at_least.unwrap_or( 0 ), - only_chain_length_at_most: self.only_chain_length_at_most.unwrap_or( !0 ), - only_chain_alive_for_at_least: self.only_chain_alive_for_at_least.unwrap_or( Duration::from_secs( 0 ) ), + only_chain_length_at_least: self.only_chain_length_at_least.unwrap_or(0), + only_chain_length_at_most: self.only_chain_length_at_most.unwrap_or(!0), + only_chain_alive_for_at_least: self + .only_chain_alive_for_at_least + .unwrap_or(Duration::from_secs(0)), only_chain_alive_for_at_most: self.only_chain_alive_for_at_most, - only_position_in_chain_at_least: self.only_position_in_chain_at_least.unwrap_or( 0 ), - only_position_in_chain_at_most: self.only_position_in_chain_at_most.unwrap_or( !0 ), + only_position_in_chain_at_least: self.only_position_in_chain_at_least.unwrap_or(0), + only_position_in_chain_at_most: self.only_position_in_chain_at_most.unwrap_or(!0), only_chain_leaked_or_deallocated_after, only_chain_deallocated_between_inclusive, - only_group_allocations_at_least: self.only_group_allocations_at_least.unwrap_or( 0 ), - only_group_allocations_at_most: self.only_group_allocations_at_most.unwrap_or( !0 ), - only_group_interval_at_least: self.only_group_interval_at_least.unwrap_or( Duration::from_secs( 0 ) ), - only_group_interval_at_most: self.only_group_interval_at_most.unwrap_or( Duration::from_secs( 5000 * 365 * 24 * 3600 ) ), - only_group_max_total_usage_first_seen_at_least: self.only_group_max_total_usage_first_seen_at_least.map( |offset| data.initial_timestamp + offset.0 ).unwrap_or( data.initial_timestamp ), - only_group_max_total_usage_first_seen_at_most: self.only_group_max_total_usage_first_seen_at_most.map( |offset| data.initial_timestamp + offset.0 ).unwrap_or( data.last_timestamp ), - only_group_leaked_allocations_at_least: self.only_group_leaked_allocations_at_least.unwrap_or( NumberOrFractionOfTotal::Number( 0 ) ), - only_group_leaked_allocations_at_most: self.only_group_leaked_allocations_at_most.unwrap_or( NumberOrFractionOfTotal::Number( !0 ) ), + only_group_allocations_at_least: self.only_group_allocations_at_least.unwrap_or(0), + only_group_allocations_at_most: self.only_group_allocations_at_most.unwrap_or(!0), + only_group_interval_at_least: self + .only_group_interval_at_least + .unwrap_or(Duration::from_secs(0)), + only_group_interval_at_most: self + .only_group_interval_at_most + .unwrap_or(Duration::from_secs(5000 * 365 * 24 * 3600)), + only_group_max_total_usage_first_seen_at_least: self + .only_group_max_total_usage_first_seen_at_least + .map(|offset| data.initial_timestamp + offset.0) + .unwrap_or(data.initial_timestamp), + only_group_max_total_usage_first_seen_at_most: self + .only_group_max_total_usage_first_seen_at_most + .map(|offset| data.initial_timestamp + offset.0) + .unwrap_or(data.last_timestamp), + only_group_leaked_allocations_at_least: self + .only_group_leaked_allocations_at_least + .unwrap_or(NumberOrFractionOfTotal::Number(0)), + only_group_leaked_allocations_at_most: self + .only_group_leaked_allocations_at_most + .unwrap_or(NumberOrFractionOfTotal::Number(!0)), enable_group_filter, - only_ptmalloc_mmaped: - if self.only_ptmalloc_mmaped { - Some( true ) - } else if self.only_ptmalloc_not_mmaped { - Some( false ) - } else { - None - }, - only_ptmalloc_from_main_arena: - if self.only_ptmalloc_from_main_arena { - Some( true ) - } else if self.only_ptmalloc_not_from_main_arena { - Some( false ) - } else { - None - }, - only_jemalloc: - if self.only_jemalloc { - Some( true ) - } else if self.only_not_jemalloc { - Some( false ) - } else { - None - }, + only_ptmalloc_mmaped: if self.only_ptmalloc_mmaped { + Some(true) + } else if self.only_ptmalloc_not_mmaped { + Some(false) + } else { + None + }, + only_ptmalloc_from_main_arena: if self.only_ptmalloc_from_main_arena { + Some(true) + } else if self.only_ptmalloc_not_from_main_arena { + Some(false) + } else { + None + }, + only_jemalloc: if self.only_jemalloc { + Some(true) + } else if self.only_not_jemalloc { + Some(false) + } else { + None + }, only_with_marker: self.only_with_marker, - only_from_maps: self.only_from_maps.as_ref().map( |only_from_maps| only_from_maps.iter().copied().collect() ), + only_from_maps: self + .only_from_maps + .as_ref() + .map(|only_from_maps| only_from_maps.iter().copied().collect()), } } } impl Compile for RawMapFilter { type Compiled = RawCompiledMapFilter; - fn compile( &self, data: &Data ) -> Self::Compiled { - let mut is_impossible = - (self.only_bytehound && self.only_not_bytehound) || - (self.only_jemalloc && self.only_not_jemalloc) || - (self.only_readable && self.only_not_readable) || - (self.only_writable && self.only_not_writable) || - (self.only_executable && self.only_not_executable); - - let only_backtraces = compile_backtrace_filter( data, &self.backtrace_filter ); - let common_filter = self.common_filter.compile( data ); + fn compile(&self, data: &Data) -> Self::Compiled { + let mut is_impossible = (self.only_bytehound && self.only_not_bytehound) + || (self.only_jemalloc && self.only_not_jemalloc) + || (self.only_readable && self.only_not_readable) + || (self.only_writable && self.only_not_writable) + || (self.only_executable && self.only_not_executable); + + let only_backtraces = compile_backtrace_filter(data, &self.backtrace_filter); + let common_filter = self.common_filter.compile(data); is_impossible = is_impossible || common_filter.is_impossible; - fn bool_filter( yes: bool, no: bool ) -> Option< bool > { + fn bool_filter(yes: bool, no: bool) -> Option { if yes { - Some( true ) + Some(true) } else if no { - Some( false ) + Some(false) } else { None } @@ -662,36 +743,45 @@ impl Compile for RawMapFilter { backtrace_filter: RawCompiledBacktraceFilter { only_backtraces, - only_not_matching_backtraces: self.backtrace_filter.only_not_matching_backtraces.clone(), - - only_deallocation_backtraces: self.backtrace_filter.only_matching_deallocation_backtraces.clone(), - only_not_matching_deallocation_backtraces: self.backtrace_filter.only_not_matching_deallocation_backtraces.clone(), + only_not_matching_backtraces: self + .backtrace_filter + .only_not_matching_backtraces + .clone(), + + only_deallocation_backtraces: self + .backtrace_filter + .only_matching_deallocation_backtraces + .clone(), + only_not_matching_deallocation_backtraces: self + .backtrace_filter + .only_not_matching_deallocation_backtraces + .clone(), }, common_filter, only_peak_rss_at_least: self.only_peak_rss_at_least, only_peak_rss_at_most: self.only_peak_rss_at_most, - jemalloc_filter: bool_filter( self.only_jemalloc, self.only_not_jemalloc ), - bytehound_filter:bool_filter( self.only_bytehound, self.only_not_bytehound ), - readable_filter:bool_filter( self.only_readable, self.only_not_readable ), - writable_filter:bool_filter( self.only_writable, self.only_not_writable ), - executable_filter:bool_filter( self.only_executable, self.only_not_executable ), + jemalloc_filter: bool_filter(self.only_jemalloc, self.only_not_jemalloc), + bytehound_filter: bool_filter(self.only_bytehound, self.only_not_bytehound), + readable_filter: bool_filter(self.only_readable, self.only_not_readable), + writable_filter: bool_filter(self.only_writable, self.only_not_writable), + executable_filter: bool_filter(self.only_executable, self.only_not_executable), } } } pub struct BacktraceFilterArgs { - pub backtrace: Option< BacktraceId >, - pub deallocation_backtrace: Option< Option< BacktraceId > > + pub backtrace: Option, + pub deallocation_backtrace: Option>, } impl TryMatch for RawCompiledBacktraceFilter { type Item = BacktraceFilterArgs; - fn try_match( &self, _: &Data, args: &BacktraceFilterArgs ) -> bool { - if let Some( ref only_backtraces ) = self.only_backtraces { - if let Some( backtrace_id ) = args.backtrace { - if !only_backtraces.contains( &backtrace_id ) { + fn try_match(&self, _: &Data, args: &BacktraceFilterArgs) -> bool { + if let Some(ref only_backtraces) = self.only_backtraces { + if let Some(backtrace_id) = args.backtrace { + if !only_backtraces.contains(&backtrace_id) { return false; } } else { @@ -699,18 +789,18 @@ impl TryMatch for RawCompiledBacktraceFilter { } } - if let Some( ref set ) = self.only_not_matching_backtraces { - if let Some( backtrace_id ) = args.backtrace { - if set.contains( &backtrace_id ) { + if let Some(ref set) = self.only_not_matching_backtraces { + if let Some(backtrace_id) = args.backtrace { + if set.contains(&backtrace_id) { return false; } } } - if let Some( ref only_deallocation_backtraces ) = self.only_deallocation_backtraces { - if let Some( deallocation ) = args.deallocation_backtrace { - if let Some( backtrace ) = deallocation { - if !only_deallocation_backtraces.contains( &backtrace ) { + if let Some(ref only_deallocation_backtraces) = self.only_deallocation_backtraces { + if let Some(deallocation) = args.deallocation_backtrace { + if let Some(backtrace) = deallocation { + if !only_deallocation_backtraces.contains(&backtrace) { return false; } } else { @@ -721,10 +811,10 @@ impl TryMatch for RawCompiledBacktraceFilter { } } - if let Some( ref set ) = self.only_not_matching_deallocation_backtraces { - if let Some( deallocation ) = args.deallocation_backtrace { - if let Some( backtrace ) = deallocation { - if set.contains( &backtrace ) { + if let Some(ref set) = self.only_not_matching_deallocation_backtraces { + if let Some(deallocation) = args.deallocation_backtrace { + if let Some(backtrace) = deallocation { + if set.contains(&backtrace) { return false; } } @@ -739,30 +829,36 @@ pub struct CommonFilterArgs { pub pointer: DataPointer, pub size: u64, pub timestamp: Timestamp, - pub deallocation_timestamp: Option< Timestamp >, + pub deallocation_timestamp: Option, } impl TryMatch for RawCompiledCommonFilter { type Item = CommonFilterArgs; - fn try_match( &self, data: &Data, allocation: &Self::Item ) -> bool { + fn try_match(&self, data: &Data, allocation: &Self::Item) -> bool { if self.is_impossible { return false; } - if !(allocation.size <= self.only_smaller_or_equal && allocation.size >= self.only_larger_or_equal) { + if !(allocation.size <= self.only_smaller_or_equal + && allocation.size >= self.only_larger_or_equal) + { return false; } - if !(allocation.pointer >= self.only_address_at_least && allocation.pointer <= self.only_address_at_most) { + if !(allocation.pointer >= self.only_address_at_least + && allocation.pointer <= self.only_address_at_most) + { return false; } - if !(allocation.timestamp >= self.only_allocated_after_at_least && allocation.timestamp <= self.only_allocated_until_at_most) { + if !(allocation.timestamp >= self.only_allocated_after_at_least + && allocation.timestamp <= self.only_allocated_until_at_most) + { return false; } - if let Some( (min, max) ) = self.only_deallocated_between_inclusive { - if let Some( deallocation_timestamp ) = allocation.deallocation_timestamp { + if let Some((min, max)) = self.only_deallocated_between_inclusive { + if let Some(deallocation_timestamp) = allocation.deallocation_timestamp { if !(deallocation_timestamp >= min && deallocation_timestamp <= max) { return false; } @@ -771,20 +867,22 @@ impl TryMatch for RawCompiledCommonFilter { } } - let lifetime_end = allocation.deallocation_timestamp.unwrap_or( data.last_timestamp() ); - let lifetime = Duration( lifetime_end - allocation.timestamp ); + let lifetime_end = allocation + .deallocation_timestamp + .unwrap_or(data.last_timestamp()); + let lifetime = Duration(lifetime_end - allocation.timestamp); if lifetime < self.only_alive_for_at_least { return false; } - if let Some( max ) = self.only_alive_for_at_most { + if let Some(max) = self.only_alive_for_at_most { if lifetime > max { return false; } } - if let Some( deallocation_timestamp ) = allocation.deallocation_timestamp { + if let Some(deallocation_timestamp) = allocation.deallocation_timestamp { if !(deallocation_timestamp > self.only_leaked_or_deallocated_after) { return false; } @@ -796,24 +894,36 @@ impl TryMatch for RawCompiledCommonFilter { impl TryMatch for RawCompiledAllocationFilter { type Item = Allocation; - fn try_match( &self, data: &Data, allocation: &Allocation ) -> bool { + fn try_match(&self, data: &Data, allocation: &Allocation) -> bool { if self.is_impossible { return false; } - if !self.common_filter.try_match( data, &CommonFilterArgs { - pointer: allocation.pointer, - size: allocation.size, - timestamp: allocation.timestamp, - deallocation_timestamp: allocation.deallocation.as_ref().map( |deallocation| deallocation.timestamp ) - }) { + if !self.common_filter.try_match( + data, + &CommonFilterArgs { + pointer: allocation.pointer, + size: allocation.size, + timestamp: allocation.timestamp, + deallocation_timestamp: allocation + .deallocation + .as_ref() + .map(|deallocation| deallocation.timestamp), + }, + ) { return false; } - if !self.backtrace_filter.try_match( data, &BacktraceFilterArgs { - backtrace: Some( allocation.backtrace ), - deallocation_backtrace: allocation.deallocation.as_ref().map( |deallocation| deallocation.backtrace ) - }) { + if !self.backtrace_filter.try_match( + data, + &BacktraceFilterArgs { + backtrace: Some(allocation.backtrace), + deallocation_backtrace: allocation + .deallocation + .as_ref() + .map(|deallocation| deallocation.backtrace), + }, + ) { return false; } @@ -824,36 +934,50 @@ impl TryMatch for RawCompiledAllocationFilter { let chain_lifetime; let chain_lifetime_end; let was_deallocated; - if let Some( first_in_chain ) = allocation.first_allocation_in_chain { - let chain = data.get_chain_by_first_allocation( first_in_chain ).unwrap(); - let first_allocation = data.get_allocation( chain.first ); - let last_allocation = data.get_allocation( chain.last ); + if let Some(first_in_chain) = allocation.first_allocation_in_chain { + let chain = data.get_chain_by_first_allocation(first_in_chain).unwrap(); + let first_allocation = data.get_allocation(chain.first); + let last_allocation = data.get_allocation(chain.last); first_allocation_size = first_allocation.size; last_allocation_size = last_allocation.size; chain_length = chain.length; - chain_lifetime_end = last_allocation.deallocation.as_ref().map( |deallocation| deallocation.timestamp ).unwrap_or( data.last_timestamp() ); - chain_lifetime = Duration( chain_lifetime_end - first_allocation.timestamp ); + chain_lifetime_end = last_allocation + .deallocation + .as_ref() + .map(|deallocation| deallocation.timestamp) + .unwrap_or(data.last_timestamp()); + chain_lifetime = Duration(chain_lifetime_end - first_allocation.timestamp); was_deallocated = last_allocation.deallocation.is_some(); } else { first_allocation_size = allocation.size; last_allocation_size = allocation.size; chain_length = 1; - chain_lifetime_end = allocation.deallocation.as_ref().map( |deallocation| deallocation.timestamp ).unwrap_or( data.last_timestamp() ); - chain_lifetime = Duration( chain_lifetime_end - allocation.timestamp ); + chain_lifetime_end = allocation + .deallocation + .as_ref() + .map(|deallocation| deallocation.timestamp) + .unwrap_or(data.last_timestamp()); + chain_lifetime = Duration(chain_lifetime_end - allocation.timestamp); was_deallocated = allocation.deallocation.is_some(); } - if !(first_allocation_size <= self.only_first_size_smaller_or_equal && first_allocation_size >= self.only_first_size_larger_or_equal) { + if !(first_allocation_size <= self.only_first_size_smaller_or_equal + && first_allocation_size >= self.only_first_size_larger_or_equal) + { return false; } - if !(last_allocation_size <= self.only_last_size_smaller_or_equal && last_allocation_size >= self.only_last_size_larger_or_equal) { + if !(last_allocation_size <= self.only_last_size_smaller_or_equal + && last_allocation_size >= self.only_last_size_larger_or_equal) + { return false; } - if !(chain_length >= self.only_chain_length_at_least && chain_length <= self.only_chain_length_at_most) { + if !(chain_length >= self.only_chain_length_at_least + && chain_length <= self.only_chain_length_at_most) + { return false; } @@ -861,7 +985,7 @@ impl TryMatch for RawCompiledAllocationFilter { return false; } - if let Some( max ) = self.only_chain_alive_for_at_most { + if let Some(max) = self.only_chain_alive_for_at_most { if chain_lifetime > max { return false; } @@ -873,7 +997,7 @@ impl TryMatch for RawCompiledAllocationFilter { } } - if let Some( (min, max) ) = self.only_chain_deallocated_between_inclusive { + if let Some((min, max)) = self.only_chain_deallocated_between_inclusive { if !was_deallocated { return false; } @@ -883,13 +1007,15 @@ impl TryMatch for RawCompiledAllocationFilter { } } - if !(allocation.position_in_chain >= self.only_position_in_chain_at_least && allocation.position_in_chain <= self.only_position_in_chain_at_most) { + if !(allocation.position_in_chain >= self.only_position_in_chain_at_least + && allocation.position_in_chain <= self.only_position_in_chain_at_most) + { return false; } } if self.enable_group_filter { - let group_allocations = data.get_allocation_ids_by_backtrace( allocation.backtrace ); + let group_allocations = data.get_allocation_ids_by_backtrace(allocation.backtrace); if group_allocations.len() < self.only_group_allocations_at_least { return false; } @@ -898,9 +1024,13 @@ impl TryMatch for RawCompiledAllocationFilter { return false; } - let first_timestamp = data.get_allocation( *group_allocations.first().unwrap() ).timestamp; - let last_timestamp = data.get_allocation( *group_allocations.last().unwrap() ).timestamp; - let interval = Duration( last_timestamp - first_timestamp ); + let first_timestamp = data + .get_allocation(*group_allocations.first().unwrap()) + .timestamp; + let last_timestamp = data + .get_allocation(*group_allocations.last().unwrap()) + .timestamp; + let interval = Duration(last_timestamp - first_timestamp); if interval < self.only_group_interval_at_least { return false; @@ -910,28 +1040,40 @@ impl TryMatch for RawCompiledAllocationFilter { return false; } - let stats = data.get_group_statistics( allocation.backtrace ); + let stats = data.get_group_statistics(allocation.backtrace); let total_allocations = stats.alloc_count as u64; let leaked = (stats.alloc_count - stats.free_count) as u64; - if leaked < self.only_group_leaked_allocations_at_least.get( total_allocations ) { + if leaked + < self + .only_group_leaked_allocations_at_least + .get(total_allocations) + { return false; } - if leaked > self.only_group_leaked_allocations_at_most.get( total_allocations ) { + if leaked + > self + .only_group_leaked_allocations_at_most + .get(total_allocations) + { return false; } - if stats.max_total_usage_first_seen_at < self.only_group_max_total_usage_first_seen_at_least { + if stats.max_total_usage_first_seen_at + < self.only_group_max_total_usage_first_seen_at_least + { return false; } - if stats.max_total_usage_first_seen_at > self.only_group_max_total_usage_first_seen_at_most { + if stats.max_total_usage_first_seen_at + > self.only_group_max_total_usage_first_seen_at_most + { return false; } } - if let Some( value ) = self.only_ptmalloc_mmaped { + if let Some(value) = self.only_ptmalloc_mmaped { if allocation.is_jemalloc() { return false; } @@ -941,7 +1083,7 @@ impl TryMatch for RawCompiledAllocationFilter { } } - if let Some( value ) = self.only_ptmalloc_from_main_arena { + if let Some(value) = self.only_ptmalloc_from_main_arena { if allocation.is_jemalloc() { return false; } @@ -951,20 +1093,23 @@ impl TryMatch for RawCompiledAllocationFilter { } } - if let Some( value ) = self.only_jemalloc { + if let Some(value) = self.only_jemalloc { if allocation.is_jemalloc() != value { return false; } } - if let Some( marker ) = self.only_with_marker { + if let Some(marker) = self.only_with_marker { if allocation.marker != marker { return false; } } - if let Some( ref only_from_maps ) = self.only_from_maps { - if !only_from_maps.iter().any( |&map_id| data.maps[ map_id.raw() as usize ].try_match_allocation( allocation ) ) { + if let Some(ref only_from_maps) = self.only_from_maps { + if !only_from_maps + .iter() + .any(|&map_id| data.maps[map_id.raw() as usize].try_match_allocation(allocation)) + { return false; } } @@ -975,65 +1120,77 @@ impl TryMatch for RawCompiledAllocationFilter { impl TryMatch for RawCompiledMapFilter { type Item = Map; - fn try_match( &self, data: &Data, map: &Map ) -> bool { + fn try_match(&self, data: &Data, map: &Map) -> bool { if self.is_impossible { return false; } - if !self.common_filter.try_match( data, &CommonFilterArgs { - pointer: map.pointer, - size: map.size, - timestamp: map.timestamp, - deallocation_timestamp: map.deallocation.as_ref().map( |deallocation| deallocation.timestamp ) - }) { + if !self.common_filter.try_match( + data, + &CommonFilterArgs { + pointer: map.pointer, + size: map.size, + timestamp: map.timestamp, + deallocation_timestamp: map + .deallocation + .as_ref() + .map(|deallocation| deallocation.timestamp), + }, + ) { return false; } - if !self.backtrace_filter.try_match( data, &BacktraceFilterArgs { - backtrace: map.source.map( |source| source.backtrace ), - deallocation_backtrace: map.deallocation.as_ref().map( |deallocation| deallocation.source.map( |source| source.backtrace ) ) - }) { + if !self.backtrace_filter.try_match( + data, + &BacktraceFilterArgs { + backtrace: map.source.map(|source| source.backtrace), + deallocation_backtrace: map + .deallocation + .as_ref() + .map(|deallocation| deallocation.source.map(|source| source.backtrace)), + }, + ) { return false; } - if let Some( rss ) = self.only_peak_rss_at_least { + if let Some(rss) = self.only_peak_rss_at_least { if !(map.peak_rss > rss) { return false; } } - if let Some( rss ) = self.only_peak_rss_at_most { + if let Some(rss) = self.only_peak_rss_at_most { if !(map.peak_rss < rss) { return false; } } - if let Some( jemalloc_filter ) = self.jemalloc_filter { + if let Some(jemalloc_filter) = self.jemalloc_filter { if (&*map.name == "[anon:jemalloc]") != jemalloc_filter { return false; } } - if let Some( bytehound_filter ) = self.bytehound_filter { + if let Some(bytehound_filter) = self.bytehound_filter { if map.is_from_bytehound() != bytehound_filter { return false; } } - if let Some( readable ) = self.readable_filter { - if map.flags.contains( crate::data::RegionFlags::READABLE ) != readable { + if let Some(readable) = self.readable_filter { + if map.flags.contains(crate::data::RegionFlags::READABLE) != readable { return false; } } - if let Some( writable ) = self.writable_filter { - if map.flags.contains( crate::data::RegionFlags::WRITABLE ) != writable { + if let Some(writable) = self.writable_filter { + if map.flags.contains(crate::data::RegionFlags::WRITABLE) != writable { return false; } } - if let Some( executable ) = self.executable_filter { - if map.flags.contains( crate::data::RegionFlags::EXECUTABLE ) != executable { + if let Some(executable) = self.executable_filter { + if map.flags.contains(crate::data::RegionFlags::EXECUTABLE) != executable { return false; } } @@ -1042,32 +1199,46 @@ impl TryMatch for RawCompiledMapFilter { } } -impl< T > TryMatch for Filter< T > where T: TryMatch { +impl TryMatch for Filter +where + T: TryMatch, +{ type Item = T::Item; - fn try_match( &self, data: &Data, allocation: &Self::Item ) -> bool { + fn try_match(&self, data: &Data, allocation: &Self::Item) -> bool { match *self { - Self::Basic( ref filter ) => filter.try_match( data, allocation ), - Self::And( ref lhs, ref rhs ) => lhs.try_match( data, allocation ) && rhs.try_match( data, allocation ), - Self::Or( ref lhs, ref rhs ) => lhs.try_match( data, allocation ) || rhs.try_match( data, allocation ), - Self::Not( ref filter ) => !filter.try_match( data, allocation ) + Self::Basic(ref filter) => filter.try_match(data, allocation), + Self::And(ref lhs, ref rhs) => { + lhs.try_match(data, allocation) && rhs.try_match(data, allocation) + } + Self::Or(ref lhs, ref rhs) => { + lhs.try_match(data, allocation) || rhs.try_match(data, allocation) + } + Self::Not(ref filter) => !filter.try_match(data, allocation), } } } -impl< T > Compile for Filter< T > where T: Compile { - type Compiled = Filter< T::Compiled >; - fn compile( &self, data: &Data ) -> Self::Compiled { +impl Compile for Filter +where + T: Compile, +{ + type Compiled = Filter; + fn compile(&self, data: &Data) -> Self::Compiled { match *self { - Filter::Basic( ref filter ) => Filter::Basic( filter.compile( data ) ), - Filter::And( ref lhs, ref rhs ) => Filter::And( Box::new( lhs.compile( data ) ), Box::new( rhs.compile( data ) ) ), - Filter::Or( ref lhs, ref rhs ) => Filter::Or( Box::new( lhs.compile( data ) ), Box::new( rhs.compile( data ) ) ), - Filter::Not( ref filter ) => Filter::Not( Box::new( filter.compile( data ) ) ) + Filter::Basic(ref filter) => Filter::Basic(filter.compile(data)), + Filter::And(ref lhs, ref rhs) => { + Filter::And(Box::new(lhs.compile(data)), Box::new(rhs.compile(data))) + } + Filter::Or(ref lhs, ref rhs) => { + Filter::Or(Box::new(lhs.compile(data)), Box::new(rhs.compile(data))) + } + Filter::Not(ref filter) => Filter::Not(Box::new(filter.compile(data))), } } } -pub type AllocationFilter = Filter< RawAllocationFilter >; -pub type CompiledAllocationFilter = Filter< RawCompiledAllocationFilter >; +pub type AllocationFilter = Filter; +pub type CompiledAllocationFilter = Filter; -pub type MapFilter = Filter< RawMapFilter >; -pub type CompiledMapFilter = Filter< RawCompiledMapFilter >; +pub type MapFilter = Filter; +pub type CompiledMapFilter = Filter; diff --git a/cli-core/src/frame.rs b/cli-core/src/frame.rs index bb8b8f99..960d5784 100644 --- a/cli-core/src/frame.rs +++ b/cli-core/src/frame.rs @@ -1,5 +1,5 @@ -use std::num::NonZeroU32; use crate::data::{CodePointer, StringId}; +use std::num::NonZeroU32; #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub struct Frame { @@ -7,17 +7,17 @@ pub struct Frame { count: u64, is_inline: bool, - library: Option< StringId >, - function: Option< StringId >, - raw_function: Option< StringId >, - source: Option< StringId >, - line: Option< NonZeroU32 >, - column: Option< NonZeroU32 > + library: Option, + function: Option, + raw_function: Option, + source: Option, + line: Option, + column: Option, } impl Frame { #[inline] - pub fn new_unknown( address: CodePointer ) -> Frame { + pub fn new_unknown(address: CodePointer) -> Frame { Frame { address, count: 0, @@ -27,89 +27,89 @@ impl Frame { raw_function: None, source: None, line: None, - column: None + column: None, } } #[inline] - pub fn address( &self ) -> CodePointer { + pub fn address(&self) -> CodePointer { self.address } #[inline] - pub fn count( &self ) -> u64 { + pub fn count(&self) -> u64 { self.count } #[inline] - pub fn is_inline( &self ) -> bool { + pub fn is_inline(&self) -> bool { self.is_inline } #[inline] - pub fn library( &self ) -> Option< StringId > { + pub fn library(&self) -> Option { self.library } #[inline] - pub fn function( &self ) -> Option< StringId > { + pub fn function(&self) -> Option { self.function } #[inline] - pub fn raw_function( &self ) -> Option< StringId > { + pub fn raw_function(&self) -> Option { self.raw_function } #[inline] - pub fn source( &self ) -> Option< StringId > { + pub fn source(&self) -> Option { self.source } #[inline] - pub fn line( &self ) -> Option< u32 > { - self.line.map( |line| line.get() ) + pub fn line(&self) -> Option { + self.line.map(|line| line.get()) } #[inline] - pub fn column( &self ) -> Option< u32 > { - self.column.map( |line| line.get() ) + pub fn column(&self) -> Option { + self.column.map(|line| line.get()) } - pub fn set_is_inline( &mut self, value: bool ) { + pub fn set_is_inline(&mut self, value: bool) { self.is_inline = value; } - pub fn set_library( &mut self, string_id: StringId ) { - self.library = Some( string_id ); + pub fn set_library(&mut self, string_id: StringId) { + self.library = Some(string_id); } - pub fn set_function( &mut self, string_id: StringId ) { - self.function = Some( string_id ); + pub fn set_function(&mut self, string_id: StringId) { + self.function = Some(string_id); } - pub fn set_raw_function( &mut self, string_id: StringId ) { - self.raw_function = Some( string_id ); + pub fn set_raw_function(&mut self, string_id: StringId) { + self.raw_function = Some(string_id); } - pub fn set_source( &mut self, string_id: StringId ) { - self.source = Some( string_id ); + pub fn set_source(&mut self, string_id: StringId) { + self.source = Some(string_id); } - pub fn set_line( &mut self, value: u32 ) { - self.line = NonZeroU32::new( value ); + pub fn set_line(&mut self, value: u32) { + self.line = NonZeroU32::new(value); } - pub fn set_column( &mut self, value: u32 ) { - self.column = NonZeroU32::new( value ); + pub fn set_column(&mut self, value: u32) { + self.column = NonZeroU32::new(value); } #[inline] - pub fn increment_count( &mut self, value: u64 ) { + pub fn increment_count(&mut self, value: u64) { self.count += value; } - pub fn any_function( &self ) -> Option< StringId > { - self.function.or( self.raw_function ) + pub fn any_function(&self) -> Option { + self.function.or(self.raw_function) } } diff --git a/cli-core/src/io_adapter.rs b/cli-core/src/io_adapter.rs index 921ee5cb..9e947fff 100644 --- a/cli-core/src/io_adapter.rs +++ b/cli-core/src/io_adapter.rs @@ -1,36 +1,41 @@ -use std::io; use std::fmt; +use std::io; use std::str; -pub struct IoAdapter< T >( T ); +pub struct IoAdapter(T); -impl< T > IoAdapter< T > { +impl IoAdapter { #[inline] - pub fn new( fp: T ) -> Self { - IoAdapter( fp ) + pub fn new(fp: T) -> Self { + IoAdapter(fp) } } -impl< T: io::Write > fmt::Write for IoAdapter< T > { +impl fmt::Write for IoAdapter { #[inline] - fn write_str( &mut self, string: &str ) -> Result< (), fmt::Error > { - self.0.write_all( string.as_bytes() ).map_err( |_| fmt::Error ) + fn write_str(&mut self, string: &str) -> Result<(), fmt::Error> { + self.0.write_all(string.as_bytes()).map_err(|_| fmt::Error) } } -impl< T: fmt::Write > io::Write for IoAdapter< T > { +impl io::Write for IoAdapter { #[inline] - fn write( &mut self, buffer: &[u8] ) -> Result< usize, io::Error > { - let string = str::from_utf8( buffer ).map_err( |err| io::Error::new( io::ErrorKind::InvalidData, err ) )?; - self.0.write_str( string ).map_err( |_| io::Error::new( io::ErrorKind::Other, "formatting error" ) )?; - Ok( buffer.len() ) + fn write(&mut self, buffer: &[u8]) -> Result { + let string = str::from_utf8(buffer) + .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; + self.0 + .write_str(string) + .map_err(|_| io::Error::new(io::ErrorKind::Other, "formatting error"))?; + Ok(buffer.len()) } - fn flush( &mut self ) -> Result< (), io::Error > { + fn flush(&mut self) -> Result<(), io::Error> { Ok(()) } - fn write_fmt( &mut self, args: fmt::Arguments ) -> Result< (), io::Error > { - self.0.write_fmt( args ).map_err( |_| io::Error::new( io::ErrorKind::Other, "formatting error" ) ) + fn write_fmt(&mut self, args: fmt::Arguments) -> Result<(), io::Error> { + self.0 + .write_fmt(args) + .map_err(|_| io::Error::new(io::ErrorKind::Other, "formatting error")) } } diff --git a/cli-core/src/lib.rs b/cli-core/src/lib.rs index 2677bd40..340c0c19 100644 --- a/cli-core/src/lib.rs +++ b/cli-core/src/lib.rs @@ -7,63 +7,57 @@ extern crate bitflags; #[macro_use] extern crate quickcheck; -pub mod cmd_gather; pub mod cmd_analyze_size; pub mod cmd_extract; +pub mod cmd_gather; -mod filter; -mod util; -mod tree; -mod tree_printer; -mod reader; -mod loader; -mod postprocessor; -mod squeeze; -mod frame; mod data; -mod io_adapter; -mod exporter_replay; -mod exporter_heaptrack; mod exporter_flamegraph; mod exporter_flamegraph_pl; -mod vecvec; -mod threaded_lz4_stream; +mod exporter_heaptrack; +mod exporter_replay; +mod filter; +mod frame; +mod io_adapter; +mod loader; +mod postprocessor; +mod reader; mod repack; -mod timeline; pub mod script; mod script_virtual; +mod squeeze; +mod threaded_lz4_stream; +mod timeline; +mod tree; +mod tree_printer; +mod util; +mod vecvec; -pub use crate::data::{Data, DataId, CodePointer, DataPointer, BacktraceId, Timestamp, Operation, OperationId, StringId, Allocation, AllocationId, FrameId, Mallopt, MalloptKind, CountAndSize, MapId, Map, RegionFlags, MapUsage, UsageDelta}; -pub use crate::loader::Loader; -pub use crate::tree::{Tree, Node, NodeId}; -pub use crate::frame::Frame; -pub use crate::exporter_replay::export_as_replay; -pub use crate::exporter_heaptrack::export_as_heaptrack; -pub use crate::exporter_flamegraph_pl::export_as_flamegraph_pl; +pub use crate::data::{ + Allocation, AllocationId, BacktraceId, CodePointer, CountAndSize, Data, DataId, DataPointer, + FrameId, Mallopt, MalloptKind, Map, MapId, MapUsage, Operation, OperationId, RegionFlags, + StringId, Timestamp, UsageDelta, +}; pub use crate::exporter_flamegraph::export_as_flamegraph; -pub use crate::vecvec::VecVec; -pub use crate::util::table_to_string; -pub use crate::postprocessor::{Anonymize, postprocess}; -pub use crate::squeeze::squeeze_data; +pub use crate::exporter_flamegraph_pl::export_as_flamegraph_pl; +pub use crate::exporter_heaptrack::export_as_heaptrack; +pub use crate::exporter_replay::export_as_replay; +pub use crate::frame::Frame; +pub use crate::loader::Loader; +pub use crate::postprocessor::{postprocess, Anonymize}; pub use crate::reader::parse_events; pub use crate::repack::repack; -pub use crate::script::{EvalOutput, run_script}; +pub use crate::script::{run_script, EvalOutput}; +pub use crate::squeeze::squeeze_data; pub use crate::timeline::{build_allocation_timeline, build_map_timeline}; +pub use crate::tree::{Node, NodeId, Tree}; +pub use crate::util::table_to_string; +pub use crate::vecvec::VecVec; pub use common::event; pub use crate::filter::{ - AllocationFilter, - CompiledAllocationFilter, - RawAllocationFilter, - RawCompiledAllocationFilter, - Duration, - Filter, - NumberOrFractionOfTotal, - Compile, - TryMatch, - MapFilter, - CompiledMapFilter, - RawMapFilter, - RawCompiledMapFilter, + AllocationFilter, Compile, CompiledAllocationFilter, CompiledMapFilter, Duration, Filter, + MapFilter, NumberOrFractionOfTotal, RawAllocationFilter, RawCompiledAllocationFilter, + RawCompiledMapFilter, RawMapFilter, TryMatch, }; diff --git a/cli-core/src/loader.rs b/cli-core/src/loader.rs index 79ac1cde..b44e78c3 100644 --- a/cli-core/src/loader.rs +++ b/cli-core/src/loader.rs @@ -1,80 +1,54 @@ -use std::mem; +use std::borrow::Cow; use std::cell::RefCell; -use std::ops::{Deref, Range}; +use std::cmp; +use std::ffi::OsStr; use std::io::{self, Read}; -use std::borrow::Cow; +use std::mem; +use std::ops::{Deref, Range}; use std::sync::Arc; use std::time::Instant; -use std::ffi::OsStr; -use std::cmp; -use std::collections::hash_map; use ahash::AHashMap as HashMap; use ahash::AHashSet as HashSet; -use byteorder::{BigEndian, LittleEndian, ByteOrder}; -use nwind::{arch, BinaryData, AddressSpace, IAddressSpace, DebugInfoIndex}; -use nwind::proc_maps::Region; +use byteorder::{BigEndian, ByteOrder, LittleEndian}; use nwind::proc_maps::parse as parse_maps; +use nwind::proc_maps::Region; +use nwind::{arch, AddressSpace, BinaryData, DebugInfoIndex, IAddressSpace}; use rayon::prelude::*; +use std::collections::hash_map; use common::event::{ - self, - Event, - HeaderBody, - AllocBody, - FramesInvalidated, - HEADER_FLAG_IS_LITTLE_ENDIAN + self, AllocBody, Event, FramesInvalidated, HeaderBody, HEADER_FLAG_IS_LITTLE_ENDIAN, }; use fast_range_map::RangeMap; -use crate::frame::Frame; use crate::data::{ - Allocation, - AllocationChain, - AllocationFlags, - AllocationId, - BacktraceId, - BacktraceStorageRef, - CodePointer, - DataPointer, - Data, - DataId, - Deallocation, - FrameId, - GroupStatistics, - Mallopt, - OperationId, - ThreadId, - Timestamp, - StringInterner, - StringId, + Allocation, AllocationChain, AllocationFlags, AllocationId, BacktraceId, BacktraceStorageRef, + CodePointer, Data, DataId, DataPointer, Deallocation, FrameId, GroupStatistics, Mallopt, Map, + MapDeallocation, MapId, MapRegion, MapRegionDeallocation, MapRegionDeallocationSource, + MapSource, MapUsage, OperationId, RegionFlags, StringId, StringInterner, ThreadId, Timestamp, UsageDelta, - Map, - MapId, - MapDeallocation, - MapRegion, - MapRegionDeallocation, - MapRegionDeallocationSource, - MapUsage, - MapSource, - RegionFlags, }; -use crate::vecvec::DenseVecVec; +use crate::frame::Frame; use crate::reader::parse_events; +use crate::vecvec::DenseVecVec; #[derive(Clone, PartialEq, Eq, Default, Debug, Hash)] pub struct AddressMapping { pub declared_address: u64, pub actual_address: u64, pub file_offset: u64, - pub size: u64 + pub size: u64, } -fn clean_symbol( input: Cow< str > ) -> Cow< str > { +fn clean_symbol(input: Cow) -> Cow { // TODO: Make this faster. input - .replace( "> >", ">>" ) - .replace( "std::__cxx11::basic_string, std::allocator>", "std::string" ) + .replace("> >", ">>") + .replace( + "std::__cxx11::basic_string, std::allocator>", + "std::string", + ) .into() } @@ -90,113 +64,118 @@ fn test_clean_symbol() { pub struct Loader { id: DataId, header: HeaderBody, - interner: RefCell< StringInterner >, - address_space: Box< dyn IAddressSpace >, + interner: RefCell, + address_space: Box, address_space_needs_reloading: bool, debug_info_index: DebugInfoIndex, - binaries: HashMap< String, Arc< BinaryData > >, - pending_address_space_maps: Vec< Region >, - address_space_maps: RangeMap< Region >, - backtraces: Vec< BacktraceStorageRef >, - backtraces_storage: Vec< FrameId >, - backtrace_to_id: HashMap< Vec< u64 >, BacktraceId >, - backtrace_remappings: HashMap< u64, BacktraceId >, - group_stats: Vec< GroupStatistics >, - operations: Vec< (Timestamp, OperationId) >, - allocations: Vec< Allocation >, - allocation_map: HashMap< (u64, u64), AllocationId >, - allocation_range_map: RangeMap< AllocationId >, + binaries: HashMap>, + pending_address_space_maps: Vec, + address_space_maps: RangeMap, + backtraces: Vec, + backtraces_storage: Vec, + backtrace_to_id: HashMap, BacktraceId>, + backtrace_remappings: HashMap, + group_stats: Vec, + operations: Vec<(Timestamp, OperationId)>, + allocations: Vec, + allocation_map: HashMap<(u64, u64), AllocationId>, + allocation_range_map: RangeMap, allocation_range_map_dirty: bool, - allocations_by_backtrace: HashMap< BacktraceId, Vec< AllocationId > >, - frames: Vec< Frame >, - frame_to_id: HashMap< Frame, FrameId >, - frames_by_address: HashMap< u64, Range< usize > >, - shared_ptr_backtraces: HashSet< BacktraceId >, - shared_ptr_allocations: HashMap< DataPointer, AllocationId >, + allocations_by_backtrace: HashMap>, + frames: Vec, + frame_to_id: HashMap, + frames_by_address: HashMap>, + shared_ptr_backtraces: HashSet, + shared_ptr_allocations: HashMap, total_allocated: u64, total_allocated_count: u64, total_freed: u64, total_freed_count: u64, - frame_skip_ranges: Vec< Range< u64 > >, - symbol_new_range: Range< u64 >, + frame_skip_ranges: Vec>, + symbol_new_range: Range, marker: u32, - mallopts: Vec< Mallopt >, + mallopts: Vec, timestamp_to_wall_clock: u64, is_little_endian: bool, maximum_backtrace_depth: u32, - previous_backtrace_on_thread: HashMap< u32, Vec< u64 > >, - string_id_map: HashMap< u32, StringId >, + previous_backtrace_on_thread: HashMap>, + string_id_map: HashMap, last_timestamp: Timestamp, - maps: Vec< Map >, - map_index_map: HashMap< u64, MapId >, - delta_list_for_map: HashMap< MapId, Vec< (Timestamp, UsageDelta) > >, - last_usage_for_region: HashMap< (MapId, usize), MapUsage >, - freshly_mmaped: HashSet< MapId >, + maps: Vec, + map_index_map: HashMap, + delta_list_for_map: HashMap>, + last_usage_for_region: HashMap<(MapId, usize), MapUsage>, + freshly_mmaped: HashSet, } -fn address_to_frame< F: FnMut( Frame ) >( address_space: &dyn IAddressSpace, interner: &mut StringInterner, address: u64, mut callback: F ) { - address_space.decode_symbol_while( address, &mut |frame| { - let mut output = Frame::new_unknown( CodePointer::new( address ) ); - if let Some( str ) = frame.library.take() { - output.set_library( interner.get_or_intern( get_basename( &str ) ) ); +fn address_to_frame( + address_space: &dyn IAddressSpace, + interner: &mut StringInterner, + address: u64, + mut callback: F, +) { + address_space.decode_symbol_while(address, &mut |frame| { + let mut output = Frame::new_unknown(CodePointer::new(address)); + if let Some(str) = frame.library.take() { + output.set_library(interner.get_or_intern(get_basename(&str))); } - if let Some( str ) = frame.demangled_name.take() { - let str = clean_symbol( str ); - output.set_function( interner.get_or_intern( str ) ); + if let Some(str) = frame.demangled_name.take() { + let str = clean_symbol(str); + output.set_function(interner.get_or_intern(str)); } - if let Some( str ) = frame.name.take() { - output.set_raw_function( interner.get_or_intern( str ) ); + if let Some(str) = frame.name.take() { + output.set_raw_function(interner.get_or_intern(str)); } - if let Some( str ) = frame.file.take() { - output.set_source( interner.get_or_intern( str ) ); + if let Some(str) = frame.file.take() { + output.set_source(interner.get_or_intern(str)); } - if let Some( value ) = frame.line.take() { - output.set_line( value as _ ); + if let Some(value) = frame.line.take() { + output.set_line(value as _); } - if let Some( value ) = frame.column.take() { - output.set_column( value as _ ); + if let Some(value) = frame.column.take() { + output.set_column(value as _); } - output.set_is_inline( frame.is_inline ); + output.set_is_inline(frame.is_inline); - callback( output ); + callback(output); true }); } -trait PointerSize: Into< u64 > { - fn read< E: ByteOrder >( slice: &[u8] ) -> Self; +trait PointerSize: Into { + fn read(slice: &[u8]) -> Self; } impl PointerSize for u32 { #[inline] - fn read< E: ByteOrder >( slice: &[u8] ) -> Self { - E::read_u32( slice ) + fn read(slice: &[u8]) -> Self { + E::read_u32(slice) } } impl PointerSize for u64 { #[inline] - fn read< E: ByteOrder >( slice: &[u8] ) -> Self { - E::read_u64( slice ) + fn read(slice: &[u8]) -> Self { + E::read_u64(slice) } } -fn get_basename( path: &str ) -> &str { +fn get_basename(path: &str) -> &str { if path.is_empty() { return path; } let path = if path.as_bytes().last().cloned().unwrap() == b'/' { - &path[ 0..path.len() - 1 ] + &path[0..path.len() - 1] } else { path }; - &path[ path.rfind( "/" ).map( |index| index + 1 ).unwrap_or( 0 ).. ] + &path[path.rfind("/").map(|index| index + 1).unwrap_or(0)..] } -fn into_key( id: event::AllocationId, pointer: DataPointer ) -> (u64, u64) { +fn into_key(id: event::AllocationId, pointer: DataPointer) -> (u64, u64) { if !id.is_invalid() && !id.is_untracked() { (id.thread, id.allocation) } else { @@ -205,13 +184,13 @@ fn into_key( id: event::AllocationId, pointer: DataPointer ) -> (u64, u64) { } impl Loader { - pub fn new( header: HeaderBody, debug_info_index: DebugInfoIndex ) -> Self { - let address_space: Box< dyn IAddressSpace > = match &*header.arch { - "arm" => Box::new( AddressSpace::< arch::arm::Arch >::new() ), - "x86_64" => Box::new( AddressSpace::< arch::amd64::Arch >::new() ), - "mips64" => Box::new( AddressSpace::< arch::mips64::Arch >::new() ), - "aarch64" => Box::new( AddressSpace::< arch::aarch64::Arch >::new() ), - _ => panic!( "Unknown architecture: {}", header.arch ) + pub fn new(header: HeaderBody, debug_info_index: DebugInfoIndex) -> Self { + let address_space: Box = match &*header.arch { + "arm" => Box::new(AddressSpace::::new()), + "x86_64" => Box::new(AddressSpace::::new()), + "mips64" => Box::new(AddressSpace::::new()), + "aarch64" => Box::new(AddressSpace::::new()), + _ => panic!("Unknown architecture: {}", header.arch), }; let flags = header.flags; @@ -222,7 +201,7 @@ impl Loader { let mut loader = Loader { id: header.id, header, - interner: RefCell::new( StringInterner::new() ), + interner: RefCell::new(StringInterner::new()), address_space, address_space_needs_reloading: true, debug_info_index, @@ -234,8 +213,8 @@ impl Loader { backtrace_to_id: Default::default(), backtrace_remappings: Default::default(), group_stats: Default::default(), - operations: Vec::with_capacity( 100000 ), - allocations: Vec::with_capacity( 100000 ), + operations: Vec::with_capacity(100000), + allocations: Vec::with_capacity(100000), allocation_map: Default::default(), allocation_range_map: RangeMap::new(), allocation_range_map_dirty: true, @@ -249,7 +228,7 @@ impl Loader { total_allocated_count: 0, total_freed: 0, total_freed_count: 0, - frame_skip_ranges: Vec::with_capacity( 4 ), + frame_skip_ranges: Vec::with_capacity(4), symbol_new_range: -1_i64 as u64..0, marker: 0, mallopts: Default::default(), @@ -266,52 +245,76 @@ impl Loader { freshly_mmaped: Default::default(), }; - loader.update_timestamp_to_wall_clock( timestamp, wall_clock_secs, wall_clock_nsecs ); + loader.update_timestamp_to_wall_clock(timestamp, wall_clock_secs, wall_clock_nsecs); loader } - fn update_timestamp_to_wall_clock( &mut self, timestamp: Timestamp, wall_clock_secs: u64, wall_clock_nsecs: u64 ) { - self.timestamp_to_wall_clock = Timestamp::from_timespec( wall_clock_secs, wall_clock_nsecs ).as_usecs().wrapping_sub( timestamp.as_usecs() ); + fn update_timestamp_to_wall_clock( + &mut self, + timestamp: Timestamp, + wall_clock_secs: u64, + wall_clock_nsecs: u64, + ) { + self.timestamp_to_wall_clock = Timestamp::from_timespec(wall_clock_secs, wall_clock_nsecs) + .as_usecs() + .wrapping_sub(timestamp.as_usecs()); } - pub fn load_from_stream_without_debug_info< F: Read + Send + 'static >( fp: F ) -> Result< Data, io::Error > { + pub fn load_from_stream_without_debug_info( + fp: F, + ) -> Result { use std::iter; - let empty: iter::Empty< &OsStr > = iter::empty(); - Loader::load_from_stream( fp, empty ) + let empty: iter::Empty<&OsStr> = iter::empty(); + Loader::load_from_stream(fp, empty) } - pub fn load_from_stream< F: Read + Send + 'static, D: AsRef< OsStr >, I: IntoIterator< Item = D > >( fp: F, debug_symbols: I ) -> Result< Data, io::Error > { - debug!( "Starting to load data..." ); + pub fn load_from_stream< + F: Read + Send + 'static, + D: AsRef, + I: IntoIterator, + >( + fp: F, + debug_symbols: I, + ) -> Result { + debug!("Starting to load data..."); let start_timestamp = Instant::now(); - let (header, event_stream) = parse_events( fp )?; + let (header, event_stream) = parse_events(fp)?; let mut debug_info_index = DebugInfoIndex::new(); for path in debug_symbols { - debug_info_index.add( path.as_ref() ); + debug_info_index.add(path.as_ref()); } - let mut loader = Loader::new( header, debug_info_index ); + let mut loader = Loader::new(header, debug_info_index); for event in event_stream { let event = event?; - loader.process( event ); + loader.process(event); } let output = loader.finalize(); let elapsed = start_timestamp.elapsed(); - info!( "Loaded data in {}s {:03}", elapsed.as_secs(), elapsed.subsec_millis() ); - Ok( output ) + info!( + "Loaded data in {}s {:03}", + elapsed.as_secs(), + elapsed.subsec_millis() + ); + Ok(output) } - fn shift_timestamp( &self, timestamp: Timestamp ) -> Timestamp { - Timestamp::from_usecs( timestamp.as_usecs().wrapping_add( self.timestamp_to_wall_clock ) ) + fn shift_timestamp(&self, timestamp: Timestamp) -> Timestamp { + Timestamp::from_usecs( + timestamp + .as_usecs() + .wrapping_add(self.timestamp_to_wall_clock), + ) } - fn parse_flags( &self, backtrace: BacktraceId, flags: u32 ) -> AllocationFlags { + fn parse_flags(&self, backtrace: BacktraceId, flags: u32) -> AllocationFlags { let mut allocation_flags = AllocationFlags::empty(); - if self.shared_ptr_backtraces.contains( &backtrace ) { + if self.shared_ptr_backtraces.contains(&backtrace) { allocation_flags |= AllocationFlags::IS_SHARED_PTR; } @@ -335,7 +338,7 @@ impl Loader { allocation_flags |= AllocationFlags::IS_JEMALLOC; } - if self.shared_ptr_backtraces.contains( &backtrace ) { + if self.shared_ptr_backtraces.contains(&backtrace) { allocation_flags |= AllocationFlags::IS_SHARED_PTR; } @@ -353,10 +356,10 @@ impl Loader { flags: u32, extra_usable_space: u32, ) { - self.last_timestamp = std::cmp::max( self.last_timestamp, timestamp ); + self.last_timestamp = std::cmp::max(self.last_timestamp, timestamp); - let flags = self.parse_flags( backtrace, flags ); - let allocation_id = AllocationId::new( self.allocations.len() as _ ); + let flags = self.parse_flags(backtrace, flags); + let allocation_id = AllocationId::new(self.allocations.len() as _); let allocation = Allocation { pointer, timestamp, @@ -370,38 +373,46 @@ impl Loader { position_in_chain: 0, flags, extra_usable_space, - marker: self.marker + marker: self.marker, }; - let key = into_key( id, pointer ); - let entry = self.allocation_map.entry( key ); - if let hash_map::Entry::Occupied( entry ) = entry { - warn!( "Duplicate allocation of 0x{:016X}; old backtrace = {:?}, new backtrace = {:?}", pointer, self.allocations[ entry.get().raw() as usize ].backtrace, backtrace ); + let key = into_key(id, pointer); + let entry = self.allocation_map.entry(key); + if let hash_map::Entry::Occupied(entry) = entry { + warn!( + "Duplicate allocation of 0x{:016X}; old backtrace = {:?}, new backtrace = {:?}", + pointer, + self.allocations[entry.get().raw() as usize].backtrace, + backtrace + ); return; } - let group_stats = &mut self.group_stats[ allocation.backtrace.raw() as usize ]; - group_stats.first_allocation = cmp::min( group_stats.first_allocation, timestamp ); - group_stats.last_allocation = cmp::max( group_stats.last_allocation, timestamp ); - group_stats.min_size = cmp::min( group_stats.min_size, allocation.usable_size() ); - group_stats.max_size = cmp::max( group_stats.max_size, allocation.usable_size() ); + let group_stats = &mut self.group_stats[allocation.backtrace.raw() as usize]; + group_stats.first_allocation = cmp::min(group_stats.first_allocation, timestamp); + group_stats.last_allocation = cmp::max(group_stats.last_allocation, timestamp); + group_stats.min_size = cmp::min(group_stats.min_size, allocation.usable_size()); + group_stats.max_size = cmp::max(group_stats.max_size, allocation.usable_size()); group_stats.alloc_count += 1; group_stats.alloc_size += allocation.usable_size(); - self.allocations.push( allocation ); - entry.or_insert( allocation_id ); + self.allocations.push(allocation); + entry.or_insert(allocation_id); self.total_allocated += size; self.total_allocated_count += 1; - let op = OperationId::new_allocation( allocation_id ); - self.operations.push( (timestamp, op) ); + let op = OperationId::new_allocation(allocation_id); + self.operations.push((timestamp, op)); - if flags.contains( AllocationFlags::IS_SHARED_PTR ) { - self.shared_ptr_allocations.insert( pointer, allocation_id ); + if flags.contains(AllocationFlags::IS_SHARED_PTR) { + self.shared_ptr_allocations.insert(pointer, allocation_id); } self.allocation_range_map_dirty = true; - self.allocations_by_backtrace.get_mut( &backtrace ).unwrap().push( allocation_id ); + self.allocations_by_backtrace + .get_mut(&backtrace) + .unwrap() + .push(allocation_id); } fn handle_free( @@ -409,33 +420,40 @@ impl Loader { id: event::AllocationId, timestamp: Timestamp, pointer: DataPointer, - backtrace: Option< BacktraceId >, - thread: ThreadId + backtrace: Option, + thread: ThreadId, ) { - self.last_timestamp = std::cmp::max( self.last_timestamp, timestamp ); + self.last_timestamp = std::cmp::max(self.last_timestamp, timestamp); - let key = into_key( id, pointer ); - let allocation_id = match self.allocation_map.remove( &key ) { - Some( id ) => id, + let key = into_key(id, pointer); + let allocation_id = match self.allocation_map.remove(&key) { + Some(id) => id, None => { - debug!( "Unknown deallocation of 0x{:016X} at backtrace = {:?}", pointer, backtrace ); + debug!( + "Unknown deallocation of 0x{:016X} at backtrace = {:?}", + pointer, backtrace + ); return; } }; - let allocation = &mut self.allocations[ allocation_id.raw() as usize ]; - allocation.deallocation = Some( Deallocation { timestamp, thread, backtrace } ); + let allocation = &mut self.allocations[allocation_id.raw() as usize]; + allocation.deallocation = Some(Deallocation { + timestamp, + thread, + backtrace, + }); self.total_freed += allocation.size; self.total_freed_count += 1; - let group_stats = &mut self.group_stats[ allocation.backtrace.raw() as usize ]; + let group_stats = &mut self.group_stats[allocation.backtrace.raw() as usize]; group_stats.free_count += 1; group_stats.free_size += allocation.usable_size(); - let op = OperationId::new_deallocation( allocation_id ); - self.operations.push( (timestamp, op) ); + let op = OperationId::new_deallocation(allocation_id); + self.operations.push((timestamp, op)); if allocation.is_shared_ptr() { - self.shared_ptr_allocations.remove( &allocation.pointer ); + self.shared_ptr_allocations.remove(&allocation.pointer); } self.allocation_range_map_dirty = true; @@ -453,26 +471,31 @@ impl Loader { flags: u32, extra_usable_space: u32, ) { - self.last_timestamp = std::cmp::max( self.last_timestamp, timestamp ); + self.last_timestamp = std::cmp::max(self.last_timestamp, timestamp); - let old_key = into_key( id, old_pointer ); - let allocation_id = match self.allocation_map.remove( &old_key ) { - Some( id ) => id, - None => return + let old_key = into_key(id, old_pointer); + let allocation_id = match self.allocation_map.remove(&old_key) { + Some(id) => id, + None => return, }; - let flags = self.parse_flags( backtrace, flags ); - let reallocation_id = AllocationId::new( self.allocations.len() as _ ); + let flags = self.parse_flags(backtrace, flags); + let reallocation_id = AllocationId::new(self.allocations.len() as _); { - let allocation = &mut self.allocations[ allocation_id.raw() as usize ]; - assert!( !allocation.is_shared_ptr() ); + let allocation = &mut self.allocations[allocation_id.raw() as usize]; + assert!(!allocation.is_shared_ptr()); - allocation.deallocation = Some( Deallocation { timestamp, thread, backtrace: Some( backtrace ) } ); - allocation.reallocation = Some( reallocation_id ); + allocation.deallocation = Some(Deallocation { + timestamp, + thread, + backtrace: Some(backtrace), + }); + allocation.reallocation = Some(reallocation_id); self.total_freed += allocation.size; self.total_freed_count += 1; - self.group_stats[ allocation.backtrace.raw() as usize ].free_count += 1; - self.group_stats[ allocation.backtrace.raw() as usize ].free_size += allocation.usable_size(); + self.group_stats[allocation.backtrace.raw() as usize].free_count += 1; + self.group_stats[allocation.backtrace.raw() as usize].free_size += + allocation.usable_size(); } let reallocation = Allocation { @@ -483,68 +506,75 @@ impl Loader { backtrace, deallocation: None, reallocation: None, - reallocated_from: Some( allocation_id ), + reallocated_from: Some(allocation_id), first_allocation_in_chain: None, position_in_chain: 0, flags, extra_usable_space, - marker: self.marker + marker: self.marker, }; - let new_key = into_key( id, new_pointer ); - let entry = self.allocation_map.entry( new_key ); - if let hash_map::Entry::Occupied( entry ) = entry { + let new_key = into_key(id, new_pointer); + let entry = self.allocation_map.entry(new_key); + if let hash_map::Entry::Occupied(entry) = entry { warn!( "Duplicate allocation (during realloc) of 0x{:016X}; old backtrace = {:?}, new backtrace = {:?}", new_pointer, self.allocations[ entry.get().raw() as usize ].backtrace, backtrace ); return; } - let group_stats = &mut self.group_stats[ reallocation.backtrace.raw() as usize ]; - group_stats.first_allocation = cmp::min( group_stats.first_allocation, timestamp ); - group_stats.last_allocation = cmp::max( group_stats.last_allocation, timestamp ); - group_stats.min_size = cmp::min( group_stats.min_size, reallocation.usable_size() ); - group_stats.max_size = cmp::max( group_stats.max_size, reallocation.usable_size() ); + let group_stats = &mut self.group_stats[reallocation.backtrace.raw() as usize]; + group_stats.first_allocation = cmp::min(group_stats.first_allocation, timestamp); + group_stats.last_allocation = cmp::max(group_stats.last_allocation, timestamp); + group_stats.min_size = cmp::min(group_stats.min_size, reallocation.usable_size()); + group_stats.max_size = cmp::max(group_stats.max_size, reallocation.usable_size()); group_stats.alloc_count += 1; group_stats.alloc_size += reallocation.usable_size(); - self.allocations.push( reallocation ); - entry.or_insert( reallocation_id ); + self.allocations.push(reallocation); + entry.or_insert(reallocation_id); self.total_allocated += size; self.total_allocated_count += 1; - let op = OperationId::new_reallocation( reallocation_id ); - self.operations.push( (timestamp, op) ); + let op = OperationId::new_reallocation(reallocation_id); + self.operations.push((timestamp, op)); self.allocation_range_map_dirty = true; - self.allocations_by_backtrace.get_mut( &backtrace ).unwrap().push( reallocation_id ); + self.allocations_by_backtrace + .get_mut(&backtrace) + .unwrap() + .push(reallocation_id); } - pub(crate) fn interner( &mut self ) -> &mut StringInterner { + pub(crate) fn interner(&mut self) -> &mut StringInterner { self.interner.get_mut() } - pub(crate) fn lookup_backtrace( &mut self, backtrace: u64 ) -> Option< BacktraceId > { - let backtrace_id = self.backtrace_remappings.get( &backtrace ).cloned()?; - self.allocations_by_backtrace.entry( backtrace_id ).or_insert( Vec::new() ); - Some( backtrace_id ) + pub(crate) fn lookup_backtrace(&mut self, backtrace: u64) -> Option { + let backtrace_id = self.backtrace_remappings.get(&backtrace).cloned()?; + self.allocations_by_backtrace + .entry(backtrace_id) + .or_insert(Vec::new()); + Some(backtrace_id) } - fn scan< P: PointerSize, B: ByteOrder >( &self, base_address: u64, data: &[u8] ) { - assert_eq!( data.len() % mem::size_of::< P >(), 0 ); - for (index, subslice) in data.chunks_exact( mem::size_of::< P >() ).enumerate() { - let value: u64 = P::read::< B >( subslice ).into(); - let allocation_id = match self.shared_ptr_allocations.get( &value ) { - Some( &value ) => value, - None => continue + fn scan(&self, base_address: u64, data: &[u8]) { + assert_eq!(data.len() % mem::size_of::

(), 0); + for (index, subslice) in data.chunks_exact(mem::size_of::

()).enumerate() { + let value: u64 = P::read::(subslice).into(); + let allocation_id = match self.shared_ptr_allocations.get(&value) { + Some(&value) => value, + None => continue, }; - let container_address = base_address + (mem::size_of::< P >() * index) as u64; - + let container_address = base_address + (mem::size_of::

() * index) as u64; - if let Some( &container_allocation_id ) = self.allocation_range_map.get_value( container_address ) { - let allocation: &Allocation = &self.allocations[ allocation_id.raw() as usize ]; - let container_allocation = &self.allocations[ container_allocation_id.raw() as usize ]; - assert!( !allocation.was_deallocated() ); - assert!( !container_allocation.was_deallocated() ); + if let Some(&container_allocation_id) = + self.allocation_range_map.get_value(container_address) + { + let allocation: &Allocation = &self.allocations[allocation_id.raw() as usize]; + let container_allocation = + &self.allocations[container_allocation_id.raw() as usize]; + assert!(!allocation.was_deallocated()); + assert!(!container_allocation.was_deallocated()); trace!( "Found an instance of shared pointer #{} (0x{:016X}) at 0x{:016X} (0x{:016X} + {}, allocation #{})", @@ -561,7 +591,7 @@ impl Loader { } } - fn reload_address_space( &mut self ) { + fn reload_address_space(&mut self) { if !self.address_space_needs_reloading { return; } @@ -571,120 +601,150 @@ impl Loader { let mut maps = Vec::new(); self.frame_skip_ranges.clear(); - for region in std::mem::take( &mut self.pending_address_space_maps ) { - if region.name.contains( "libmemory_profiler" ) || region.name.contains( "libbytehound" ) { - if self.frame_skip_ranges.last().map( |last_range| last_range.end == region.start ).unwrap_or( false ) { + for region in std::mem::take(&mut self.pending_address_space_maps) { + if region.name.contains("libmemory_profiler") || region.name.contains("libbytehound") { + if self + .frame_skip_ranges + .last() + .map(|last_range| last_range.end == region.start) + .unwrap_or(false) + { let mut last_range = self.frame_skip_ranges.last_mut().unwrap(); last_range.end = region.end; } else { - self.frame_skip_ranges.push( region.start..region.end ); + self.frame_skip_ranges.push(region.start..region.end); } } - maps.push( (region.start..region.end, region) ); + maps.push((region.start..region.end, region)); } for range in &self.frame_skip_ranges { - debug!( "Skip range: 0x{:016X}-0x{:016X}", range.start, range.end ); + debug!("Skip range: 0x{:016X}-0x{:016X}", range.start, range.end); } - self.address_space_maps = RangeMap::from_vec( maps ); - let binaries: Vec< _ > = self.binaries.values().cloned().collect(); + self.address_space_maps = RangeMap::from_vec(maps); + let binaries: Vec<_> = self.binaries.values().cloned().collect(); for binary_data in binaries { - self.scan_for_symbols( &binary_data ); + self.scan_for_symbols(&binary_data); } let binaries = &self.binaries; let debug_info_index = &mut self.debug_info_index; - let regions: Vec< Region > = self.address_space_maps.values().cloned().collect(); - self.address_space.reload( regions, &mut |region, handle| { - handle.should_load_frame_descriptions( false ); - - let basename = get_basename( ®ion.name ); - let debug_binary_data = if let Some( binary_data ) = binaries.get( ®ion.name ).cloned() { - let debug_binary_data = debug_info_index.get( &basename, binary_data.debuglink(), binary_data.build_id() ); - handle.set_binary( binary_data ); + let regions: Vec = self.address_space_maps.values().cloned().collect(); + self.address_space.reload(regions, &mut |region, handle| { + handle.should_load_frame_descriptions(false); + + let basename = get_basename(®ion.name); + let debug_binary_data = if let Some(binary_data) = binaries.get(®ion.name).cloned() { + let debug_binary_data = debug_info_index.get( + &basename, + binary_data.debuglink(), + binary_data.build_id(), + ); + handle.set_binary(binary_data); debug_binary_data } else { - debug_info_index.get( &basename, None, None ) + debug_info_index.get(&basename, None, None) }; - if let Some( debug_binary_data ) = debug_binary_data { - handle.set_debug_binary( debug_binary_data.clone() ); + if let Some(debug_binary_data) = debug_binary_data { + handle.set_debug_binary(debug_binary_data.clone()); } }); } - fn scan_for_symbols( &mut self, binary_data: &BinaryData ) { + fn scan_for_symbols(&mut self, binary_data: &BinaryData) { if self.maps.is_empty() { return; } - ::nwind::Symbols::each_from_binary_data( &binary_data, |range, name| { + ::nwind::Symbols::each_from_binary_data(&binary_data, |range, name| { if name == "_Znwm" { - let region = self.address_space_maps.values().find( |region| { - region.name == binary_data.name() - }); + let region = self + .address_space_maps + .values() + .find(|region| region.name == binary_data.name()); - if let Some( region ) = region { + if let Some(region) = region { self.symbol_new_range = region.start + range.start..region.start + range.end; } } }); } - fn handle_backtrace( &mut self, id: BacktraceId, potentially_call_to_new: bool ) { - let (offset, length) = self.backtraces[ id.raw() as usize ]; - self.maximum_backtrace_depth = cmp::max( self.maximum_backtrace_depth, length as _ ); + fn handle_backtrace(&mut self, id: BacktraceId, potentially_call_to_new: bool) { + let (offset, length) = self.backtraces[id.raw() as usize]; + self.maximum_backtrace_depth = cmp::max(self.maximum_backtrace_depth, length as _); if potentially_call_to_new { let interner = self.interner.get_mut(); let frames = &self.frames; - let mut iter = self.backtraces_storage[ offset as usize.. ].iter().rev().flat_map( |&id| frames[ id ].raw_function().and_then( |id| interner.resolve( id ) ) ); - if let Some( name ) = iter.next() { + let mut iter = self.backtraces_storage[offset as usize..] + .iter() + .rev() + .flat_map(|&id| { + frames[id] + .raw_function() + .and_then(|id| interner.resolve(id)) + }); + if let Some(name) = iter.next() { if name == "_ZNSt16_Sp_counted_baseILN9__gnu_cxx12_Lock_policyE2EEC4Ev" { - self.shared_ptr_backtraces.insert( id ); + self.shared_ptr_backtraces.insert(id); } } } - self.allocations_by_backtrace.entry( id ).or_insert( Vec::new() ); + self.allocations_by_backtrace + .entry(id) + .or_insert(Vec::new()); - assert_eq!( self.group_stats.len(), id.raw() as usize ); - self.group_stats.push( Default::default() ); + assert_eq!(self.group_stats.len(), id.raw() as usize); + self.group_stats.push(Default::default()); } - fn add_backtrace< F >( &mut self, raw_id: u64, addresses: Cow< [u64] >, mut callback: F ) -> Option< BacktraceId > where F: FnMut( FrameId, bool ) { + fn add_backtrace( + &mut self, + raw_id: u64, + addresses: Cow<[u64]>, + mut callback: F, + ) -> Option + where + F: FnMut(FrameId, bool), + { self.reload_address_space(); let mut is_call_to_new = false; - let mut to_skip = addresses.iter().take_while( |&&address| { - let address = address - 1; - for range in &self.frame_skip_ranges { - if address >= range.start && address <= range.end { - return true; + let mut to_skip = addresses + .iter() + .take_while(|&&address| { + let address = address - 1; + for range in &self.frame_skip_ranges { + if address >= range.start && address <= range.end { + return true; + } } - } - if address >= self.symbol_new_range.start && address < self.symbol_new_range.end { - is_call_to_new = true; - return true; - } + if address >= self.symbol_new_range.start && address < self.symbol_new_range.end { + is_call_to_new = true; + return true; + } - false - }).count(); + false + }) + .count(); if to_skip > 0 { to_skip -= 1; } - if let Some( target_id ) = self.backtrace_to_id.get( &addresses[ to_skip.. ] ).cloned() { - self.backtrace_remappings.insert( raw_id, target_id ); + if let Some(target_id) = self.backtrace_to_id.get(&addresses[to_skip..]).cloned() { + self.backtrace_remappings.insert(raw_id, target_id); return None; } let mut addresses = addresses.into_owned(); - addresses.drain( 0..to_skip ); + addresses.drain(0..to_skip); let backtrace_storage_offset = self.backtraces_storage.len(); let backtrace_storage = &mut self.backtraces_storage; @@ -693,164 +753,220 @@ impl Loader { let mut interner = self.interner.get_mut(); for (index, &address) in addresses.iter().enumerate() { - let is_bytehound_tail = index == 0 && self.frame_skip_ranges.iter().any( |range| address >= range.start && address <= range.end ); + let is_bytehound_tail = index == 0 + && self + .frame_skip_ranges + .iter() + .any(|range| address >= range.start && address <= range.end); let address = if address > 0 { address - 1 } else { 0 }; - if let Some( range ) = self.frames_by_address.get( &address ).cloned() { + if let Some(range) = self.frames_by_address.get(&address).cloned() { for index in range { - let frame_id = backtrace_storage[ index ]; - backtrace_storage.push( frame_id ); - callback( frame_id, false ); + let frame_id = backtrace_storage[index]; + backtrace_storage.push(frame_id); + callback(frame_id, false); } } else { let offset = backtrace_storage.len(); - address_to_frame( &*self.address_space, &mut interner, address, |frame| { - let (frame_id, is_new) = if let Some( &frame_id ) = frame_to_id.get( &frame ) { + address_to_frame(&*self.address_space, &mut interner, address, |frame| { + let (frame_id, is_new) = if let Some(&frame_id) = frame_to_id.get(&frame) { (frame_id, false) } else { let frame_id = frames.len(); - frame_to_id.insert( frame.clone(), frame_id ); - frames.push( frame ); + frame_to_id.insert(frame.clone(), frame_id); + frames.push(frame); (frame_id, true) }; - callback( frame_id, is_new ); - backtrace_storage.push( frame_id ); + callback(frame_id, is_new); + backtrace_storage.push(frame_id); }); if is_bytehound_tail && backtrace_storage.len() > offset { - backtrace_storage.drain( offset..backtrace_storage.len() - 1 ); + backtrace_storage.drain(offset..backtrace_storage.len() - 1); } - self.frames_by_address.insert( address, offset..backtrace_storage.len() ); + self.frames_by_address + .insert(address, offset..backtrace_storage.len()); } } - let id = BacktraceId::new( self.backtraces.len() as _ ); - self.backtrace_remappings.insert( raw_id, id ); + let id = BacktraceId::new(self.backtraces.len() as _); + self.backtrace_remappings.insert(raw_id, id); let backtrace_length = backtrace_storage.len() - backtrace_storage_offset; let backtrace_storage_ref = (backtrace_storage_offset as _, backtrace_length as _); - self.backtrace_to_id.insert( addresses, id ); - self.backtraces.push( backtrace_storage_ref ); + self.backtrace_to_id.insert(addresses, id); + self.backtraces.push(backtrace_storage_ref); - self.handle_backtrace( id, is_call_to_new ); - Some( id ) + self.handle_backtrace(id, is_call_to_new); + Some(id) } pub(crate) fn expand_partial_backtrace( - previous_backtrace_on_thread: &mut HashMap< u32, Vec< u64 > >, + previous_backtrace_on_thread: &mut HashMap>, thread: u32, frames_invalidated: FramesInvalidated, - partial_addresses: impl ExactSizeIterator< Item = u64 > - ) -> Vec< u64 > { + partial_addresses: impl ExactSizeIterator, + ) -> Vec { match frames_invalidated { FramesInvalidated::All => { let mut addresses = Vec::new(); mem::swap( - previous_backtrace_on_thread.entry( thread ).or_insert( Vec::new() ), - &mut addresses + previous_backtrace_on_thread + .entry(thread) + .or_insert(Vec::new()), + &mut addresses, ); addresses.clear(); - addresses.extend( partial_addresses ); + addresses.extend(partial_addresses); addresses - }, - FramesInvalidated::Some( frames_invalidated ) => { - let old_addresses = previous_backtrace_on_thread.entry( thread ).or_insert( Vec::new() ); + } + FramesInvalidated::Some(frames_invalidated) => { + let old_addresses = previous_backtrace_on_thread + .entry(thread) + .or_insert(Vec::new()); let new_iter = partial_addresses; - let old_iter = old_addresses.iter().cloned().skip( frames_invalidated as usize ); - new_iter.chain( old_iter ).collect() + let old_iter = old_addresses + .iter() + .cloned() + .skip(frames_invalidated as usize); + new_iter.chain(old_iter).collect() } } } - pub(crate) fn process_backtrace_event< F >( &mut self, event: Event, callback: F ) -> Option< BacktraceId > where F: FnMut( FrameId, bool ) { + pub(crate) fn process_backtrace_event( + &mut self, + event: Event, + callback: F, + ) -> Option + where + F: FnMut(FrameId, bool), + { match event { - Event::PartialBacktrace { id: raw_id, thread, frames_invalidated, addresses: partial_addresses } => { + Event::PartialBacktrace { + id: raw_id, + thread, + frames_invalidated, + addresses: partial_addresses, + } => { let addresses = Self::expand_partial_backtrace( &mut self.previous_backtrace_on_thread, thread, frames_invalidated, - partial_addresses.iter().cloned() + partial_addresses.iter().cloned(), ); - let backtrace_id = self.add_backtrace( raw_id, addresses.as_slice().into(), callback ); - *self.previous_backtrace_on_thread.get_mut( &thread ).unwrap() = addresses; + let backtrace_id = + self.add_backtrace(raw_id, addresses.as_slice().into(), callback); + *self.previous_backtrace_on_thread.get_mut(&thread).unwrap() = addresses; backtrace_id - }, - Event::PartialBacktrace32 { id: raw_id, thread, frames_invalidated, addresses: partial_addresses } => { + } + Event::PartialBacktrace32 { + id: raw_id, + thread, + frames_invalidated, + addresses: partial_addresses, + } => { let addresses = Self::expand_partial_backtrace( &mut self.previous_backtrace_on_thread, thread, frames_invalidated, - partial_addresses.iter().cloned().map( |value| value as u64 ) + partial_addresses.iter().cloned().map(|value| value as u64), ); - let backtrace_id = self.add_backtrace( raw_id, addresses.as_slice().into(), callback ); - *self.previous_backtrace_on_thread.get_mut( &thread ).unwrap() = addresses; + let backtrace_id = + self.add_backtrace(raw_id, addresses.as_slice().into(), callback); + *self.previous_backtrace_on_thread.get_mut(&thread).unwrap() = addresses; backtrace_id - }, - Event::Backtrace { id: raw_id, addresses } => { - self.add_backtrace( raw_id, addresses, callback ) - }, - Event::Backtrace32 { id: raw_id, addresses } => { - self.add_backtrace( raw_id, addresses.iter().map( |&p| p as u64 ).collect(), callback ) - }, + } + Event::Backtrace { + id: raw_id, + addresses, + } => self.add_backtrace(raw_id, addresses, callback), + Event::Backtrace32 { + id: raw_id, + addresses, + } => self.add_backtrace( + raw_id, + addresses.iter().map(|&p| p as u64).collect(), + callback, + ), _ => { unreachable!(); } } } - pub(crate) fn get_frame( &self, id: FrameId ) -> &Frame { - &self.frames[ id ] + pub(crate) fn get_frame(&self, id: FrameId) -> &Frame { + &self.frames[id] } - pub fn process( &mut self, event: Event ) { + pub fn process(&mut self, event: Event) { match event { - Event::Header( header ) => { - assert_eq!( header.id, self.header.id ); - assert_eq!( header.initial_timestamp, self.header.initial_timestamp ); - }, - Event::File { ref path, ref contents, .. } | Event::File64 { ref path, ref contents, .. } if path == "/proc/self/maps" => { - let contents = String::from_utf8_lossy( &contents ); - trace!( "/proc/self/maps:\n{}", contents ); - - self.pending_address_space_maps = parse_maps( &contents ); + Event::Header(header) => { + assert_eq!(header.id, self.header.id); + assert_eq!(header.initial_timestamp, self.header.initial_timestamp); + } + Event::File { + ref path, + ref contents, + .. + } + | Event::File64 { + ref path, + ref contents, + .. + } if path == "/proc/self/maps" => { + let contents = String::from_utf8_lossy(&contents); + trace!("/proc/self/maps:\n{}", contents); + + self.pending_address_space_maps = parse_maps(&contents); self.address_space_needs_reloading = true; - }, - Event::MemoryMapEx { map_id, address, requested_length, mmap_protection, mmap_flags, source, requested_address: _, file_descriptor: _, offset: _ } => { - let backtrace = self.lookup_backtrace( source.backtrace ).unwrap(); + } + Event::MemoryMapEx { + map_id, + address, + requested_length, + mmap_protection, + mmap_flags, + source, + requested_address: _, + file_descriptor: _, + offset: _, + } => { + let backtrace = self.lookup_backtrace(source.backtrace).unwrap(); let source = MapSource { - timestamp: self.shift_timestamp( source.timestamp ), + timestamp: self.shift_timestamp(source.timestamp), backtrace, - thread: source.thread + thread: source.thread, }; - self.last_timestamp = std::cmp::max( self.last_timestamp, source.timestamp ); + self.last_timestamp = std::cmp::max(self.last_timestamp, source.timestamp); let mut flags = RegionFlags::empty(); if mmap_protection & 0x1 != 0 { - flags.insert( RegionFlags::READABLE ); + flags.insert(RegionFlags::READABLE); } if mmap_protection & 0x2 != 0 { - flags.insert( RegionFlags::WRITABLE ); + flags.insert(RegionFlags::WRITABLE); } if mmap_protection & 0x4 != 0 { - flags.insert( RegionFlags::EXECUTABLE ); + flags.insert(RegionFlags::EXECUTABLE); } if mmap_flags & 0x1 != 0 { - flags.insert( RegionFlags::SHARED ); + flags.insert(RegionFlags::SHARED); } - let id = MapId( self.maps.len() as _ ); - self.map_index_map.insert( map_id, id ); + let id = MapId(self.maps.len() as _); + self.map_index_map.insert(map_id, id); let map = Map { id, timestamp: source.timestamp, - source: Some( source ), + source: Some(source), deallocation: None, regions: Default::default(), // This will get populated later. usage_history: Default::default(), @@ -858,35 +974,52 @@ impl Loader { size: requested_length, flags, name: Default::default(), // This will get populated later. - peak_rss: 0, // This will get populated later. + peak_rss: 0, // This will get populated later. }; - self.maps.push( map ); - self.freshly_mmaped.insert( id ); - self.delta_list_for_map.insert( id, vec![ (source.timestamp, UsageDelta { - address_space: requested_length as i64, - anonymous: 0, - shared_clean: 0, - shared_dirty: 0, - private_clean: 0, - private_dirty: 0, - swap: 0 - })]); - }, - Event::AddRegion { timestamp, map_id, address, length, file_offset, inode, major, minor, flags, name } => { - let mut timestamp = self.shift_timestamp( timestamp ); - self.last_timestamp = std::cmp::max( self.last_timestamp, timestamp ); + self.maps.push(map); + self.freshly_mmaped.insert(id); + self.delta_list_for_map.insert( + id, + vec![( + source.timestamp, + UsageDelta { + address_space: requested_length as i64, + anonymous: 0, + shared_clean: 0, + shared_dirty: 0, + private_clean: 0, + private_dirty: 0, + swap: 0, + }, + )], + ); + } + Event::AddRegion { + timestamp, + map_id, + address, + length, + file_offset, + inode, + major, + minor, + flags, + name, + } => { + let mut timestamp = self.shift_timestamp(timestamp); + self.last_timestamp = std::cmp::max(self.last_timestamp, timestamp); let map: &mut Map; let delta_list; - if let Some( &existing_id ) = self.map_index_map.get( &map_id ) { - map = &mut self.maps[ existing_id.0 as usize ]; - delta_list = self.delta_list_for_map.get_mut( &existing_id ).unwrap(); + if let Some(&existing_id) = self.map_index_map.get(&map_id) { + map = &mut self.maps[existing_id.0 as usize]; + delta_list = self.delta_list_for_map.get_mut(&existing_id).unwrap(); - if self.freshly_mmaped.remove( &existing_id ) { + if self.freshly_mmaped.remove(&existing_id) { // The region matches what we've mmaped; correct the timestamp. if map.pointer == address && map.size == length && map.flags == flags { - assert!( timestamp >= map.timestamp ); + assert!(timestamp >= map.timestamp); timestamp = map.timestamp; } @@ -895,22 +1028,25 @@ impl Loader { // If the regions did not actually change this will essentially be a no-op // since we'll be adding the address space substracted here again during all // of these `AddRegion`s. - delta_list.push( (timestamp, UsageDelta { - address_space: map.size as i64 * -1, - anonymous: 0, - shared_clean: 0, - shared_dirty: 0, - private_clean: 0, - private_dirty: 0, - swap: 0 - })); + delta_list.push(( + timestamp, + UsageDelta { + address_space: map.size as i64 * -1, + anonymous: 0, + shared_clean: 0, + shared_dirty: 0, + private_clean: 0, + private_dirty: 0, + swap: 0, + }, + )); map.name = (&*name).into(); } } else { - let id = MapId( self.maps.len() as _ ); - self.map_index_map.insert( map_id, id ); - self.maps.push( Map { + let id = MapId(self.maps.len() as _); + self.map_index_map.insert(map_id, id); + self.maps.push(Map { id, timestamp, source: None, @@ -921,10 +1057,10 @@ impl Loader { pointer: address, size: length, flags, - name: (&*name).into() + name: (&*name).into(), }); map = self.maps.last_mut().unwrap(); - delta_list = self.delta_list_for_map.entry( id ).or_insert_with( Vec::new ); + delta_list = self.delta_list_for_map.entry(id).or_insert_with(Vec::new); } let region = MapRegion { @@ -937,7 +1073,7 @@ impl Loader { major, minor, name: name.into(), - deallocation: None + deallocation: None, }; let usage = MapUsage { @@ -948,103 +1084,167 @@ impl Loader { shared_dirty: 0, private_clean: 0, private_dirty: 0, - swap: 0 + swap: 0, }; - trace!( "Add region: id = {} ({}), address = 0x{:016X}, length = {}, source = {}", self.map_index_map.get( &map_id ).unwrap().raw(), map_id, address, length, map.source.is_some() ); + trace!( + "Add region: id = {} ({}), address = 0x{:016X}, length = {}, source = {}", + self.map_index_map.get(&map_id).unwrap().raw(), + map_id, + address, + length, + map.source.is_some() + ); - self.last_usage_for_region.insert( (map.id, map.regions.len()), usage ); - map.regions.push( region ); - delta_list.push( (timestamp, UsageDelta { - address_space: length as i64, - anonymous: 0, - shared_clean: 0, - shared_dirty: 0, - private_clean: 0, - private_dirty: 0, - swap: 0 - })); - }, - Event::RemoveRegion { timestamp, map_id, address, length, sources } => { - let timestamp = self.shift_timestamp( timestamp ); - self.last_timestamp = std::cmp::max( self.last_timestamp, timestamp ); - - let id = match self.map_index_map.get( &map_id ) { - Some( smap ) => *smap, + self.last_usage_for_region + .insert((map.id, map.regions.len()), usage); + map.regions.push(region); + delta_list.push(( + timestamp, + UsageDelta { + address_space: length as i64, + anonymous: 0, + shared_clean: 0, + shared_dirty: 0, + private_clean: 0, + private_dirty: 0, + swap: 0, + }, + )); + } + Event::RemoveRegion { + timestamp, + map_id, + address, + length, + sources, + } => { + let timestamp = self.shift_timestamp(timestamp); + self.last_timestamp = std::cmp::max(self.last_timestamp, timestamp); + + let id = match self.map_index_map.get(&map_id) { + Some(smap) => *smap, None => { - warn!( "Remove map for map which was not found: id = {}, address = 0x{:016X}", map_id, address ); + warn!( + "Remove map for map which was not found: id = {}, address = 0x{:016X}", + map_id, address + ); return; } }; - let sources = sources.iter().map( |source| { - let backtrace = self.lookup_backtrace( source.source.backtrace ).unwrap(); - MapRegionDeallocationSource { - address: source.address, - length: source.length, - source: MapSource { - timestamp: self.shift_timestamp( source.source.timestamp ), - backtrace, - thread: source.source.thread + let sources = sources + .iter() + .map(|source| { + let backtrace = self.lookup_backtrace(source.source.backtrace).unwrap(); + MapRegionDeallocationSource { + address: source.address, + length: source.length, + source: MapSource { + timestamp: self.shift_timestamp(source.source.timestamp), + backtrace, + thread: source.source.thread, + }, } - } - }).collect(); - - let map = &mut self.maps[ id.0 as usize ]; - let (region_index, region) = match map.regions.iter_mut().enumerate().rev().find( |(_, region)| region.pointer == address && region.size == length ) { - Some( region ) => region, + }) + .collect(); + + let map = &mut self.maps[id.0 as usize]; + let (region_index, region) = match map + .regions + .iter_mut() + .enumerate() + .rev() + .find(|(_, region)| region.pointer == address && region.size == length) + { + Some(region) => region, None => { warn!( "Remove map for region which was not found: id = {}, address = 0x{:016X}, length = {}", map_id, address, length ); - return + return; } }; - trace!( "Remove region: id = {} ({}), address = 0x{:016X}, length = {}", id.0, map_id, address, length ); + trace!( + "Remove region: id = {} ({}), address = 0x{:016X}, length = {}", + id.0, + map_id, + address, + length + ); if region.size != length { - warn!( "Length doesn't match for region at 0x{:016X} region removal", address ); + warn!( + "Length doesn't match for region at 0x{:016X} region removal", + address + ); } - region.deallocation = Some( MapRegionDeallocation { - timestamp, - sources - }); + region.deallocation = Some(MapRegionDeallocation { timestamp, sources }); - let last_usage = self.last_usage_for_region.remove( &(map.id, region_index) ).unwrap(); - let delta_list = self.delta_list_for_map.get_mut( &map.id ).unwrap(); - delta_list.push( (timestamp, UsageDelta { - address_space: -(region.size as i64), - anonymous: -(last_usage.anonymous as i64), - shared_clean: -(last_usage.shared_clean as i64), - shared_dirty: -(last_usage.shared_dirty as i64), - private_clean: -(last_usage.private_clean as i64), - private_dirty: -(last_usage.private_dirty as i64), - swap: -(last_usage.swap as i64) - })); - }, - Event::UpdateRegionUsage { timestamp, map_id, address, length, anonymous, shared_clean, shared_dirty, private_clean, private_dirty, swap } => { - let timestamp = self.shift_timestamp( timestamp ); - self.last_timestamp = std::cmp::max( self.last_timestamp, timestamp ); - - let id = match self.map_index_map.get( &map_id ) { - Some( smap ) => *smap, + let last_usage = self + .last_usage_for_region + .remove(&(map.id, region_index)) + .unwrap(); + let delta_list = self.delta_list_for_map.get_mut(&map.id).unwrap(); + delta_list.push(( + timestamp, + UsageDelta { + address_space: -(region.size as i64), + anonymous: -(last_usage.anonymous as i64), + shared_clean: -(last_usage.shared_clean as i64), + shared_dirty: -(last_usage.shared_dirty as i64), + private_clean: -(last_usage.private_clean as i64), + private_dirty: -(last_usage.private_dirty as i64), + swap: -(last_usage.swap as i64), + }, + )); + } + Event::UpdateRegionUsage { + timestamp, + map_id, + address, + length, + anonymous, + shared_clean, + shared_dirty, + private_clean, + private_dirty, + swap, + } => { + let timestamp = self.shift_timestamp(timestamp); + self.last_timestamp = std::cmp::max(self.last_timestamp, timestamp); + + let id = match self.map_index_map.get(&map_id) { + Some(smap) => *smap, None => { - warn!( "Update for map which was not found: id = {}, address = 0x{:016X}", map_id, address ); + warn!( + "Update for map which was not found: id = {}, address = 0x{:016X}", + map_id, address + ); return; } }; - let map = &mut self.maps[ id.0 as usize ]; - let (region_index, region) = match map.regions.iter_mut().enumerate().rev().find( |(_, region)| region.pointer == address ) { - Some( region_index ) => region_index, + let map = &mut self.maps[id.0 as usize]; + let (region_index, region) = match map + .regions + .iter_mut() + .enumerate() + .rev() + .find(|(_, region)| region.pointer == address) + { + Some(region_index) => region_index, None => { warn!( "Update for a map for region which was not found: id = {}, address = 0x{:016X}", map_id, address ); - return + return; } }; if region.size != length { - warn!( "Length doesn't match for region at 0x{:016X} for region update", address ); + warn!( + "Length doesn't match for region at 0x{:016X} for region update", + address + ); return; } @@ -1059,175 +1259,340 @@ impl Loader { swap: swap * 1024, }; - let last_usage = self.last_usage_for_region.get_mut( &(map.id, region_index) ).unwrap(); - let delta_list = self.delta_list_for_map.get_mut( &map.id ).unwrap(); - delta_list.push( (timestamp, UsageDelta { - address_space: 0, - anonymous: usage.anonymous as i64 - last_usage.anonymous as i64, - shared_clean: usage.shared_clean as i64 - last_usage.shared_clean as i64, - shared_dirty: usage.shared_dirty as i64 - last_usage.shared_dirty as i64, - private_clean: usage.private_clean as i64 - last_usage.private_clean as i64, - private_dirty: usage.private_dirty as i64 - last_usage.private_dirty as i64, - swap: usage.swap as i64 - last_usage.swap as i64 - })); + let last_usage = self + .last_usage_for_region + .get_mut(&(map.id, region_index)) + .unwrap(); + let delta_list = self.delta_list_for_map.get_mut(&map.id).unwrap(); + delta_list.push(( + timestamp, + UsageDelta { + address_space: 0, + anonymous: usage.anonymous as i64 - last_usage.anonymous as i64, + shared_clean: usage.shared_clean as i64 - last_usage.shared_clean as i64, + shared_dirty: usage.shared_dirty as i64 - last_usage.shared_dirty as i64, + private_clean: usage.private_clean as i64 - last_usage.private_clean as i64, + private_dirty: usage.private_dirty as i64 - last_usage.private_dirty as i64, + swap: usage.swap as i64 - last_usage.swap as i64, + }, + )); *last_usage = usage; - }, - Event::File { ref path, ref contents, .. } | Event::File64 { ref path, ref contents, .. } => { - if !contents.starts_with( b"\x7FELF" ) { + } + Event::File { + ref path, + ref contents, + .. + } + | Event::File64 { + ref path, + ref contents, + .. + } => { + if !contents.starts_with(b"\x7FELF") { return; } - trace!( "File: {}", path ); - if let Ok( binary_data ) = BinaryData::load_from_owned_bytes( &path, contents.clone().into_owned() ) { - self.scan_for_symbols( &binary_data ); - self.binaries.insert( path.deref().to_owned(), Arc::new( binary_data ) ); + trace!("File: {}", path); + if let Ok(binary_data) = + BinaryData::load_from_owned_bytes(&path, contents.clone().into_owned()) + { + self.scan_for_symbols(&binary_data); + self.binaries + .insert(path.deref().to_owned(), Arc::new(binary_data)); } - }, - event @ Event::PartialBacktrace { .. } | - event @ Event::PartialBacktrace32 { .. } | - event @ Event::Backtrace { .. } | - event @ Event::Backtrace32 { .. } => { - self.process_backtrace_event( event, |_, _| {} ); - }, + } + event @ Event::PartialBacktrace { .. } + | event @ Event::PartialBacktrace32 { .. } + | event @ Event::Backtrace { .. } + | event @ Event::Backtrace32 { .. } => { + self.process_backtrace_event(event, |_, _| {}); + } Event::String { id, string } => { - let target_id = self.interner.get_mut().get_or_intern( string ); - self.string_id_map.insert( id, target_id ); - }, - Event::DecodedFrame { address, library, raw_function, function, source, line, column, is_inline } => { - let mut frame = Frame::new_unknown( CodePointer::new( address ) ); + let target_id = self.interner.get_mut().get_or_intern(string); + self.string_id_map.insert(id, target_id); + } + Event::DecodedFrame { + address, + library, + raw_function, + function, + source, + line, + column, + is_inline, + } => { + let mut frame = Frame::new_unknown(CodePointer::new(address)); if library != 0xFFFFFFFF { - frame.set_library( *self.string_id_map.get( &library ).unwrap() ); + frame.set_library(*self.string_id_map.get(&library).unwrap()); } if raw_function != 0xFFFFFFFF { - frame.set_raw_function( *self.string_id_map.get( &raw_function ).unwrap() ); + frame.set_raw_function(*self.string_id_map.get(&raw_function).unwrap()); } if function != 0xFFFFFFFF { - frame.set_function( *self.string_id_map.get( &function ).unwrap() ); + frame.set_function(*self.string_id_map.get(&function).unwrap()); } if source != 0xFFFFFFFF { - frame.set_source( *self.string_id_map.get( &source ).unwrap() ); + frame.set_source(*self.string_id_map.get(&source).unwrap()); } if line != 0xFFFFFFFF { - frame.set_line( line ); + frame.set_line(line); } if column != 0xFFFFFFFF { - frame.set_column( column ); + frame.set_column(column); } - frame.set_is_inline( is_inline ); - self.frames.push( frame ); - }, + frame.set_is_inline(is_inline); + self.frames.push(frame); + } Event::DecodedBacktrace { frames } => { - let id = BacktraceId::new( self.backtraces.len() as _ ); - self.backtrace_remappings.insert( id.raw() as _, id ); + let id = BacktraceId::new(self.backtraces.len() as _); + self.backtrace_remappings.insert(id.raw() as _, id); let backtrace_storage_offset = self.backtraces_storage.len(); - self.backtraces_storage.extend( frames.iter().cloned().map( |id| id as usize ) ); + self.backtraces_storage + .extend(frames.iter().cloned().map(|id| id as usize)); let backtrace_length = self.backtraces_storage.len() - backtrace_storage_offset; let backtrace_storage_ref = (backtrace_storage_offset as _, backtrace_length as _); - self.backtraces.push( backtrace_storage_ref ); - self.handle_backtrace( id, true ); - }, - Event::Alloc { timestamp, allocation: AllocBody { pointer, size, backtrace, thread, flags, extra_usable_space, preceding_free_space: _ } } => { - let timestamp = self.shift_timestamp( timestamp ); - let backtrace = self.lookup_backtrace( backtrace ).unwrap(); - self.handle_alloc( event::AllocationId::UNTRACKED, timestamp, pointer, size, backtrace, thread, flags, extra_usable_space ); - }, - Event::AllocEx { id, timestamp, allocation: AllocBody { pointer, size, backtrace, thread, flags, extra_usable_space, preceding_free_space: _ } } => { - let timestamp = self.shift_timestamp( timestamp ); - let backtrace = self.lookup_backtrace( backtrace ).unwrap(); - self.handle_alloc( id, timestamp, pointer, size, backtrace, thread, flags, extra_usable_space ); - }, - Event::Realloc { timestamp, old_pointer, allocation: AllocBody { pointer, size, backtrace, thread, flags, extra_usable_space, preceding_free_space: _ } } => { - let timestamp = self.shift_timestamp( timestamp ); - let backtrace = self.lookup_backtrace( backtrace ).unwrap(); - self.handle_realloc( event::AllocationId::UNTRACKED, timestamp, old_pointer, pointer, size, backtrace, thread, flags, extra_usable_space ); - }, - Event::ReallocEx { id, timestamp, old_pointer, allocation: AllocBody { pointer, size, backtrace, thread, flags, extra_usable_space, preceding_free_space: _ } } => { - let timestamp = self.shift_timestamp( timestamp ); - let backtrace = self.lookup_backtrace( backtrace ).unwrap(); - self.handle_realloc( id, timestamp, old_pointer, pointer, size, backtrace, thread, flags, extra_usable_space ); - }, - Event::Free { timestamp, pointer, backtrace, thread } => { - let timestamp = self.shift_timestamp( timestamp ); - let backtrace = self.lookup_backtrace( backtrace ); - self.handle_free( event::AllocationId::UNTRACKED, timestamp, pointer, backtrace, thread ); - }, - Event::FreeEx { id, timestamp, pointer, backtrace, thread } => { - let timestamp = self.shift_timestamp( timestamp ); - let backtrace = self.lookup_backtrace( backtrace ); - self.handle_free( id, timestamp, pointer, backtrace, thread ); - }, + self.backtraces.push(backtrace_storage_ref); + self.handle_backtrace(id, true); + } + Event::Alloc { + timestamp, + allocation: + AllocBody { + pointer, + size, + backtrace, + thread, + flags, + extra_usable_space, + preceding_free_space: _, + }, + } => { + let timestamp = self.shift_timestamp(timestamp); + let backtrace = self.lookup_backtrace(backtrace).unwrap(); + self.handle_alloc( + event::AllocationId::UNTRACKED, + timestamp, + pointer, + size, + backtrace, + thread, + flags, + extra_usable_space, + ); + } + Event::AllocEx { + id, + timestamp, + allocation: + AllocBody { + pointer, + size, + backtrace, + thread, + flags, + extra_usable_space, + preceding_free_space: _, + }, + } => { + let timestamp = self.shift_timestamp(timestamp); + let backtrace = self.lookup_backtrace(backtrace).unwrap(); + self.handle_alloc( + id, + timestamp, + pointer, + size, + backtrace, + thread, + flags, + extra_usable_space, + ); + } + Event::Realloc { + timestamp, + old_pointer, + allocation: + AllocBody { + pointer, + size, + backtrace, + thread, + flags, + extra_usable_space, + preceding_free_space: _, + }, + } => { + let timestamp = self.shift_timestamp(timestamp); + let backtrace = self.lookup_backtrace(backtrace).unwrap(); + self.handle_realloc( + event::AllocationId::UNTRACKED, + timestamp, + old_pointer, + pointer, + size, + backtrace, + thread, + flags, + extra_usable_space, + ); + } + Event::ReallocEx { + id, + timestamp, + old_pointer, + allocation: + AllocBody { + pointer, + size, + backtrace, + thread, + flags, + extra_usable_space, + preceding_free_space: _, + }, + } => { + let timestamp = self.shift_timestamp(timestamp); + let backtrace = self.lookup_backtrace(backtrace).unwrap(); + self.handle_realloc( + id, + timestamp, + old_pointer, + pointer, + size, + backtrace, + thread, + flags, + extra_usable_space, + ); + } + Event::Free { + timestamp, + pointer, + backtrace, + thread, + } => { + let timestamp = self.shift_timestamp(timestamp); + let backtrace = self.lookup_backtrace(backtrace); + self.handle_free( + event::AllocationId::UNTRACKED, + timestamp, + pointer, + backtrace, + thread, + ); + } + Event::FreeEx { + id, + timestamp, + pointer, + backtrace, + thread, + } => { + let timestamp = self.shift_timestamp(timestamp); + let backtrace = self.lookup_backtrace(backtrace); + self.handle_free(id, timestamp, pointer, backtrace, thread); + } Event::MemoryMap { .. } => { // Not used anymore. - }, + } Event::MemoryUnmap { .. } => { // Not used anymore. - }, - Event::Mallopt { timestamp, backtrace, thread, param, value, result } => { - let timestamp = self.shift_timestamp( timestamp ); - let backtrace = self.lookup_backtrace( backtrace ).unwrap(); + } + Event::Mallopt { + timestamp, + backtrace, + thread, + param, + value, + result, + } => { + let timestamp = self.shift_timestamp(timestamp); + let backtrace = self.lookup_backtrace(backtrace).unwrap(); let kind = param.into(); let mallopt = Mallopt { - timestamp, backtrace, + timestamp, + backtrace, thread, kind, value, - result + result, }; - self.mallopts.push( mallopt ); - }, + self.mallopts.push(mallopt); + } Event::Environ { .. } => { // TODO - }, - Event::WallClock { timestamp, sec, nsec } => { - self.update_timestamp_to_wall_clock( timestamp, sec, nsec ); - }, + } + Event::WallClock { + timestamp, + sec, + nsec, + } => { + self.update_timestamp_to_wall_clock(timestamp, sec, nsec); + } Event::Marker { value } => { self.marker = value; - }, - Event::MemoryDump { address, length, data } => { + } + Event::MemoryDump { + address, + length, + data, + } => { if true { // TODO return; } if self.allocation_range_map_dirty { - let mut allocations: Vec< (Range< u64 >, AllocationId) > = Vec::with_capacity( self.allocations.len() ); + let mut allocations: Vec<(Range, AllocationId)> = + Vec::with_capacity(self.allocations.len()); for (allocation_id, allocation) in self.allocations.iter().enumerate() { - let allocation_id = AllocationId::new( allocation_id as _ ); + let allocation_id = AllocationId::new(allocation_id as _); if allocation.was_deallocated() { continue; } - allocations.push( (allocation.pointer..allocation.pointer + allocation.size, allocation_id) ); + allocations.push(( + allocation.pointer..allocation.pointer + allocation.size, + allocation_id, + )); } let count = allocations.len(); - self.allocation_range_map = RangeMap::from_vec( allocations ); + self.allocation_range_map = RangeMap::from_vec(allocations); self.allocation_range_map_dirty = false; - assert_eq!( count, self.allocation_range_map.len() ); + assert_eq!(count, self.allocation_range_map.len()); } let length = length as usize; - assert_eq!( data.len(), length ); + assert_eq!(data.len(), length); match (self.header.pointer_size, self.is_little_endian) { - (4, false) => self.scan::< u32, BigEndian >( address, &data ), - (4, true) => self.scan::< u32, LittleEndian >( address, &data ), - (8, false) => self.scan::< u64, BigEndian >( address, &data ), - (8, true) => self.scan::< u64, LittleEndian >( address, &data ), - _ => unreachable!() + (4, false) => self.scan::(address, &data), + (4, true) => self.scan::(address, &data), + (8, false) => self.scan::(address, &data), + (8, true) => self.scan::(address, &data), + _ => unreachable!(), } - }, - Event::GroupStatistics { backtrace, first_allocation, last_allocation, free_count, free_size, min_size, max_size } => { - let first_allocation = self.shift_timestamp( first_allocation ); - let last_allocation = self.shift_timestamp( last_allocation ); - let backtrace = self.lookup_backtrace( backtrace ).unwrap(); - let group_stats = &mut self.group_stats[ backtrace.raw() as usize ]; - group_stats.first_allocation = cmp::min( group_stats.first_allocation, first_allocation ); - group_stats.last_allocation = cmp::max( group_stats.last_allocation, last_allocation ); - group_stats.min_size = cmp::min( group_stats.min_size, min_size ); - group_stats.max_size = cmp::max( group_stats.max_size, max_size ); + } + Event::GroupStatistics { + backtrace, + first_allocation, + last_allocation, + free_count, + free_size, + min_size, + max_size, + } => { + let first_allocation = self.shift_timestamp(first_allocation); + let last_allocation = self.shift_timestamp(last_allocation); + let backtrace = self.lookup_backtrace(backtrace).unwrap(); + let group_stats = &mut self.group_stats[backtrace.raw() as usize]; + group_stats.first_allocation = + cmp::min(group_stats.first_allocation, first_allocation); + group_stats.last_allocation = + cmp::max(group_stats.last_allocation, last_allocation); + group_stats.min_size = cmp::min(group_stats.min_size, min_size); + group_stats.max_size = cmp::max(group_stats.max_size, max_size); group_stats.alloc_count += free_count; group_stats.alloc_size += free_size; group_stats.free_count += free_count; @@ -1236,37 +1601,40 @@ impl Loader { } } - pub fn finalize( mut self ) -> Data { - std::mem::take( &mut self.last_usage_for_region ); + pub fn finalize(mut self) -> Data { + std::mem::take(&mut self.last_usage_for_region); let mut chains = HashMap::new(); for index in 0..self.allocations.len() { - let mut allocation = &self.allocations[ index ]; + let mut allocation = &self.allocations[index]; if allocation.reallocation.is_some() || allocation.reallocated_from.is_none() { continue; } - let last_allocation_id = AllocationId::new( index as _ ); + let last_allocation_id = AllocationId::new(index as _); let mut first_allocation_id = last_allocation_id; let mut chain_length = 1; - while let Some( previous_id ) = allocation.reallocated_from { + while let Some(previous_id) = allocation.reallocated_from { first_allocation_id = previous_id; - allocation = &self.allocations[ previous_id.raw() as usize ]; + allocation = &self.allocations[previous_id.raw() as usize]; chain_length += 1; } - chains.insert( first_allocation_id, AllocationChain { - first: first_allocation_id, - last: last_allocation_id, - length: chain_length - }); + chains.insert( + first_allocation_id, + AllocationChain { + first: first_allocation_id, + last: last_allocation_id, + length: chain_length, + }, + ); let mut current = first_allocation_id; for position in 0.. { - let alloc = &mut self.allocations[ current.raw() as usize ]; - alloc.first_allocation_in_chain = Some( first_allocation_id ); + let alloc = &mut self.allocations[current.raw() as usize]; + alloc.first_allocation_in_chain = Some(first_allocation_id); alloc.position_in_chain = position; - if let Some( next_id ) = alloc.reallocation { + if let Some(next_id) = alloc.reallocation { current = next_id; } else { break; @@ -1274,22 +1642,32 @@ impl Loader { } } - let initial_timestamp = self.shift_timestamp( self.header.initial_timestamp ); - let indices: Vec< AllocationId > = (0..self.allocations.len()).into_iter().map( |id| AllocationId::new( id as _ ) ).collect(); + let initial_timestamp = self.shift_timestamp(self.header.initial_timestamp); + let indices: Vec = (0..self.allocations.len()) + .into_iter() + .map(|id| AllocationId::new(id as _)) + .collect(); for (raw_backtrace_id, stats) in self.group_stats.iter().enumerate() { - let (backtrace_offset, backtrace_len) = self.backtraces[ raw_backtrace_id ]; - for &frame_id in &self.backtraces_storage[ backtrace_offset as usize..(backtrace_offset + backtrace_len) as usize ] { - self.frames[ frame_id ].increment_count( stats.alloc_count ); + let (backtrace_offset, backtrace_len) = self.backtraces[raw_backtrace_id]; + for &frame_id in &self.backtraces_storage + [backtrace_offset as usize..(backtrace_offset + backtrace_len) as usize] + { + self.frames[frame_id].increment_count(stats.alloc_count); } } - fn cmp_by_time( allocations: &[Allocation], a_id: AllocationId, b_id: AllocationId ) -> std::cmp::Ordering { - let a_alloc = &allocations[ a_id.raw() as usize ]; - let b_alloc = &allocations[ b_id.raw() as usize ]; - a_alloc.timestamp.cmp( &b_alloc.timestamp ).then_with( || - a_id.raw().cmp( &b_id.raw() ) - ) + fn cmp_by_time( + allocations: &[Allocation], + a_id: AllocationId, + b_id: AllocationId, + ) -> std::cmp::Ordering { + let a_alloc = &allocations[a_id.raw() as usize]; + let b_alloc = &allocations[b_id.raw() as usize]; + a_alloc + .timestamp + .cmp(&b_alloc.timestamp) + .then_with(|| a_id.raw().cmp(&b_id.raw())) } let mut sorted_by_timestamp = indices.clone(); @@ -1297,39 +1675,41 @@ impl Loader { let mut sorted_by_size = indices; { let allocations = &self.allocations; - sorted_by_timestamp.par_sort_by( |&a_id, &b_id| cmp_by_time( allocations, a_id, b_id ) ); - sorted_by_address.par_sort_by_key( |index| allocations[ index.raw() as usize ].pointer ); - sorted_by_size.par_sort_by_key( |index| allocations[ index.raw() as usize ].size ); + sorted_by_timestamp.par_sort_by(|&a_id, &b_id| cmp_by_time(allocations, a_id, b_id)); + sorted_by_address.par_sort_by_key(|index| allocations[index.raw() as usize].pointer); + sorted_by_size.par_sort_by_key(|index| allocations[index.raw() as usize].size); } - self.operations.par_sort_by_key( |(timestamp, _)| *timestamp ); - let operations: Vec< _ > = self.operations.into_iter().map( |(_, op)| op ).collect(); + self.operations.par_sort_by_key(|(timestamp, _)| *timestamp); + let operations: Vec<_> = self.operations.into_iter().map(|(_, op)| op).collect(); let mut current_total_usage_by_backtrace = Vec::new(); - current_total_usage_by_backtrace.resize( self.backtraces.len(), 0 ); + current_total_usage_by_backtrace.resize(self.backtraces.len(), 0); let mut current_total_max_size_by_backtrace = Vec::new(); - current_total_max_size_by_backtrace.resize( self.backtraces.len(), (0, initial_timestamp) ); + current_total_max_size_by_backtrace.resize(self.backtraces.len(), (0, initial_timestamp)); for &op in &operations { - let allocation = &self.allocations[ op.id().raw() as usize ]; + let allocation = &self.allocations[op.id().raw() as usize]; let backtrace = allocation.backtrace; - let mut current = current_total_usage_by_backtrace[ backtrace.raw() as usize ]; + let mut current = current_total_usage_by_backtrace[backtrace.raw() as usize]; if op.is_deallocation() { current -= allocation.usable_size() as isize; } else if op.is_allocation() { current += allocation.usable_size() as isize; } else if op.is_reallocation() { - let old_allocation = &self.allocations[ allocation.reallocated_from.unwrap().raw() as usize ]; + let old_allocation = + &self.allocations[allocation.reallocated_from.unwrap().raw() as usize]; current += allocation.usable_size() as isize; current -= old_allocation.usable_size() as isize; } - if current > current_total_max_size_by_backtrace[ backtrace.raw() as usize ].0 { - current_total_max_size_by_backtrace[ backtrace.raw() as usize ] = (current, allocation.timestamp); + if current > current_total_max_size_by_backtrace[backtrace.raw() as usize].0 { + current_total_max_size_by_backtrace[backtrace.raw() as usize] = + (current, allocation.timestamp); } - current_total_usage_by_backtrace[ backtrace.raw() as usize ] = current; + current_total_usage_by_backtrace[backtrace.raw() as usize] = current; } self.allocations.shrink_to_fit(); @@ -1341,51 +1721,71 @@ impl Loader { self.maps.shrink_to_fit(); for (index, (_, timestamp)) in current_total_max_size_by_backtrace.into_iter().enumerate() { - self.group_stats[ index ].max_total_usage_first_seen_at = timestamp; + self.group_stats[index].max_total_usage_first_seen_at = timestamp; } let mut allocations_by_backtrace = DenseVecVec::new(); - let mut index: Vec< _ > = self.allocations_by_backtrace.into_iter().collect(); - index.sort_by_key( |&(k, _)| k ); + let mut index: Vec<_> = self.allocations_by_backtrace.into_iter().collect(); + index.sort_by_key(|&(k, _)| k); let allocations = &self.allocations; for (backtrace_id, mut allocation_ids) in index { - debug_assert!( allocation_ids.is_empty() || allocations[ allocation_ids[ 0 ].raw() as usize ].backtrace == backtrace_id ); - allocation_ids.sort_by( |&a_id, &b_id| cmp_by_time( allocations, a_id, b_id ) ); - let index = allocations_by_backtrace.push( allocation_ids ); - assert_eq!( index, backtrace_id.raw() as usize ); + debug_assert!( + allocation_ids.is_empty() + || allocations[allocation_ids[0].raw() as usize].backtrace == backtrace_id + ); + allocation_ids.sort_by(|&a_id, &b_id| cmp_by_time(allocations, a_id, b_id)); + let index = allocations_by_backtrace.push(allocation_ids); + assert_eq!(index, backtrace_id.raw() as usize); } allocations_by_backtrace.shrink_to_fit(); let delta_list_for_map = self.delta_list_for_map; - self.maps.par_iter_mut().for_each( |map| { - let raw_delta_list = delta_list_for_map.get( &map.id ).unwrap(); + self.maps.par_iter_mut().for_each(|map| { + let raw_delta_list = delta_list_for_map.get(&map.id).unwrap(); let mut new_delta_list = raw_delta_list.clone(); - new_delta_list.sort_by_key( |(timestamp, _)| *timestamp ); + new_delta_list.sort_by_key(|(timestamp, _)| *timestamp); for index in 0..new_delta_list.len() - 1 { - if new_delta_list[ index ].0 == new_delta_list[ index + 1 ].0 { - let delta = std::mem::take( &mut new_delta_list[ index ].1 ); - new_delta_list[ index + 1 ].1 += delta; + if new_delta_list[index].0 == new_delta_list[index + 1].0 { + let delta = std::mem::take(&mut new_delta_list[index].1); + new_delta_list[index + 1].1 += delta; } } - new_delta_list.retain( |(_, delta)| *delta != UsageDelta::default() ); + new_delta_list.retain(|(_, delta)| *delta != UsageDelta::default()); if map.regions.len() == 1 { - map.deallocation = map.regions[ 0 ].deallocation.as_ref().map( |deallocation| { - MapDeallocation { - timestamp: deallocation.timestamp, - source: deallocation.sources.first().map( |source| source.source.clone() ) - } - }); + map.deallocation = + map.regions[0] + .deallocation + .as_ref() + .map(|deallocation| MapDeallocation { + timestamp: deallocation.timestamp, + source: deallocation + .sources + .first() + .map(|source| source.source.clone()), + }); } else { - if map.regions.iter().all( |region| region.deallocation.is_some() ) { - let deallocation = map.regions.iter().map( |region| region.deallocation.as_ref().unwrap() ).max_by_key( |deallocation| deallocation.timestamp ).unwrap(); - map.deallocation = Some( MapDeallocation { + if map + .regions + .iter() + .all(|region| region.deallocation.is_some()) + { + let deallocation = map + .regions + .iter() + .map(|region| region.deallocation.as_ref().unwrap()) + .max_by_key(|deallocation| deallocation.timestamp) + .unwrap(); + map.deallocation = Some(MapDeallocation { timestamp: deallocation.timestamp, - source: deallocation.sources.first().map( |source| source.source.clone() ) + source: deallocation + .sources + .first() + .map(|source| source.source.clone()), }); } } @@ -1402,7 +1802,7 @@ impl Loader { }; let mut peak_rss = 0; - let mut usage_history = Vec::with_capacity( new_delta_list.len() ); + let mut usage_history = Vec::with_capacity(new_delta_list.len()); for (timestamp, delta) in new_delta_list.into_iter() { let usage = MapUsage { timestamp, @@ -1415,23 +1815,28 @@ impl Loader { swap: (last_usage.swap as i64 + delta.swap) as u64, }; - peak_rss = std::cmp::max( peak_rss, usage.rss() ); + peak_rss = std::cmp::max(peak_rss, usage.rss()); last_usage = usage.clone(); - usage_history.push( usage ); + usage_history.push(usage); } map.usage_history = usage_history; map.peak_rss = peak_rss; }); - let last_timestamp = self.group_stats.iter().map( |stats| stats.last_allocation ).max().unwrap_or( initial_timestamp ); - let last_timestamp = std::cmp::max( self.last_timestamp, last_timestamp ); + let last_timestamp = self + .group_stats + .iter() + .map(|stats| stats.last_allocation) + .max() + .unwrap_or(initial_timestamp); + let last_timestamp = std::cmp::max(self.last_timestamp, last_timestamp); Data { id: self.id, initial_timestamp, last_timestamp, - executable: String::from_utf8_lossy( &self.header.executable ).into_owned(), - cmdline: String::from_utf8_lossy( &self.header.cmdline ).into_owned(), + executable: String::from_utf8_lossy(&self.header.executable).into_owned(), + cmdline: String::from_utf8_lossy(&self.header.cmdline).into_owned(), architecture: self.header.arch, pointer_size: self.header.pointer_size as _, interner: self.interner.into_inner(), @@ -1452,7 +1857,7 @@ impl Loader { maximum_backtrace_depth: self.maximum_backtrace_depth, group_stats: self.group_stats, chains, - map_ids: (0..self.maps.len()).map( |id| MapId( id as u64 ) ).collect(), + map_ids: (0..self.maps.len()).map(|id| MapId(id as u64)).collect(), maps: self.maps, } } diff --git a/cli-core/src/postprocessor.rs b/cli-core/src/postprocessor.rs index 24b1e6a5..ac1fdb93 100644 --- a/cli-core/src/postprocessor.rs +++ b/cli-core/src/postprocessor.rs @@ -1,28 +1,17 @@ -use std::io::{self, Read, Write}; +use std::borrow::Cow; use std::ffi::OsStr; +use std::io::{self, Read, Write}; use std::u64; -use std::borrow::Cow; use ahash::AHashSet as HashSet; use string_interner::Symbol; -use nwind::{ - DebugInfoIndex -}; -use common::speedy::{ - Writable, -}; +use common::speedy::Writable; +use nwind::DebugInfoIndex; -use common::event::{ - Event, - AllocBody, - HeaderBody, - RegionSource, -}; +use common::event::{AllocBody, Event, HeaderBody, RegionSource}; -use common::lz4_stream::{ - Lz4Writer -}; +use common::lz4_stream::Lz4Writer; use crate::loader::Loader; use crate::reader::parse_events; @@ -31,19 +20,24 @@ use crate::reader::parse_events; pub enum Anonymize { None, Partial, - Full + Full, } -fn anonymize_header( anonymize: Anonymize, header: &mut HeaderBody ) { +fn anonymize_header(anonymize: Anonymize, header: &mut HeaderBody) { match anonymize { - Anonymize::None => {}, + Anonymize::None => {} Anonymize::Partial => { - if let Some( index ) = header.executable.iter().rev().position( |&byte| byte == b'/' ) { - header.executable = header.executable[ header.executable.len() - index.. ].into(); + if let Some(index) = header + .executable + .iter() + .rev() + .position(|&byte| byte == b'/') + { + header.executable = header.executable[header.executable.len() - index..].into(); } header.cmdline = header.executable.clone(); - }, + } Anonymize::Full => { header.executable = b"program"[..].to_owned(); header.cmdline = header.executable.clone(); @@ -53,32 +47,34 @@ fn anonymize_header( anonymize: Anonymize, header: &mut HeaderBody ) { struct PathAnonymizer { prefix: &'static str, - counter: usize + counter: usize, } impl PathAnonymizer { - fn new( prefix: &'static str ) -> Self { - Self { - prefix, - counter: 0 - } + fn new(prefix: &'static str) -> Self { + Self { prefix, counter: 0 } } - fn anonymize< 'a >( &mut self, anonymize: Anonymize, string: &'a str ) -> Cow< 'a, str > { + fn anonymize<'a>(&mut self, anonymize: Anonymize, string: &'a str) -> Cow<'a, str> { match anonymize { Anonymize::None => string.into(), Anonymize::Partial => { - if let Some( index ) = string.as_bytes().iter().rev().position( |&byte| byte == b'/' ) { - string[ string.len() - index.. ].into() + if let Some(index) = string + .as_bytes() + .iter() + .rev() + .position(|&byte| byte == b'/') + { + string[string.len() - index..].into() } else { string.into() } - }, + } Anonymize::Full => { let counter = self.counter; self.counter += 1; - format!( "{}{}", self.prefix, counter ).into() + format!("{}{}", self.prefix, counter).into() } } } @@ -86,42 +82,48 @@ impl PathAnonymizer { #[derive(Default)] struct FunctionAnonymizer { - counter: usize + counter: usize, } impl FunctionAnonymizer { - fn anonymize< 'a >( &mut self, anonymize: Anonymize, string: &'a str ) -> Cow< 'a, str > { + fn anonymize<'a>(&mut self, anonymize: Anonymize, string: &'a str) -> Cow<'a, str> { if anonymize != Anonymize::Full { string.into() } else { let counter = self.counter; self.counter += 1; - format!( "fn_{}", counter ).into() + format!("fn_{}", counter).into() } } } -pub fn postprocess< F, G, D, I >( ifp: F, ofp: G, debug_symbols: I, anonymize: Anonymize ) -> Result< (), io::Error > - where F: Read + Send + 'static, - G: Write, - D: AsRef< OsStr >, - I: IntoIterator< Item = D > +pub fn postprocess( + ifp: F, + ofp: G, + debug_symbols: I, + anonymize: Anonymize, +) -> Result<(), io::Error> +where + F: Read + Send + 'static, + G: Write, + D: AsRef, + I: IntoIterator, { - let mut ofp = Lz4Writer::new( ofp ); - let (mut header, event_stream) = parse_events( ifp )?; + let mut ofp = Lz4Writer::new(ofp); + let (mut header, event_stream) = parse_events(ifp)?; let mut debug_info_index = DebugInfoIndex::new(); for path in debug_symbols { - debug_info_index.add( path.as_ref() ); + debug_info_index.add(path.as_ref()); } - let mut loader = Loader::new( header.clone(), debug_info_index ); - anonymize_header( anonymize, &mut header ); - Event::Header( header ).write_to_stream( &mut ofp )?; + let mut loader = Loader::new(header.clone(), debug_info_index); + anonymize_header(anonymize, &mut header); + Event::Header(header).write_to_stream(&mut ofp)?; - let mut anonymizer_library = PathAnonymizer::new( "lib_" ); - let mut anonymizer_source = PathAnonymizer::new( "src_" ); + let mut anonymizer_library = PathAnonymizer::new("lib_"); + let mut anonymizer_source = PathAnonymizer::new("src_"); let mut anonymizer_function = FunctionAnonymizer::default(); let mut frames = Vec::new(); @@ -135,62 +137,106 @@ pub fn postprocess< F, G, D, I >( ifp: F, ofp: G, debug_symbols: I, anonymize: let mut is_backtrace = false; let mut write = true; match event { - Event::Backtrace { .. } | - Event::Backtrace32 { .. } => { + Event::Backtrace { .. } | Event::Backtrace32 { .. } => { is_backtrace = true; write = false; - }, - Event::PartialBacktrace { .. } | - Event::PartialBacktrace32 { .. } => { + } + Event::PartialBacktrace { .. } | Event::PartialBacktrace32 { .. } => { is_backtrace = true; write = false; - }, - Event::Alloc { allocation: AllocBody { ref mut backtrace, .. }, .. } | - Event::AllocEx { allocation: AllocBody { ref mut backtrace, .. }, .. } | - Event::Realloc { allocation: AllocBody { ref mut backtrace, .. }, .. } | - Event::ReallocEx { allocation: AllocBody { ref mut backtrace, .. }, .. } | - Event::Free { ref mut backtrace, .. } | - Event::FreeEx { ref mut backtrace, .. } | - Event::MemoryMap { ref mut backtrace, .. } | - Event::MemoryUnmap { ref mut backtrace, .. } | - Event::Mallopt { ref mut backtrace, .. } | - Event::GroupStatistics { ref mut backtrace, .. } => { - if let Some( target_backtrace ) = loader.lookup_backtrace( *backtrace ) { + } + Event::Alloc { + allocation: AllocBody { + ref mut backtrace, .. + }, + .. + } + | Event::AllocEx { + allocation: AllocBody { + ref mut backtrace, .. + }, + .. + } + | Event::Realloc { + allocation: AllocBody { + ref mut backtrace, .. + }, + .. + } + | Event::ReallocEx { + allocation: AllocBody { + ref mut backtrace, .. + }, + .. + } + | Event::Free { + ref mut backtrace, .. + } + | Event::FreeEx { + ref mut backtrace, .. + } + | Event::MemoryMap { + ref mut backtrace, .. + } + | Event::MemoryUnmap { + ref mut backtrace, .. + } + | Event::Mallopt { + ref mut backtrace, .. + } + | Event::GroupStatistics { + ref mut backtrace, .. + } => { + if let Some(target_backtrace) = loader.lookup_backtrace(*backtrace) { *backtrace = target_backtrace.raw() as _; } else { *backtrace = u64::MAX; } - }, + } - Event::File { ref mut contents, .. } | Event::File64 { ref mut contents, .. } if contents.starts_with( b"\x7FELF" ) => { + Event::File { + ref mut contents, .. + } + | Event::File64 { + ref mut contents, .. + } if contents.starts_with(b"\x7FELF") => { process = true; write = false; - }, + } Event::File { .. } | Event::File64 { .. } => { process = true; if anonymize != Anonymize::None { write = false; } - }, - Event::MemoryMapEx { source: RegionSource { ref mut backtrace, .. }, .. } => { - if let Some( target_backtrace ) = loader.lookup_backtrace( *backtrace ) { + } + Event::MemoryMapEx { + source: RegionSource { + ref mut backtrace, .. + }, + .. + } => { + if let Some(target_backtrace) = loader.lookup_backtrace(*backtrace) { *backtrace = target_backtrace.raw() as _; } else { *backtrace = u64::MAX; } - }, + } Event::AddRegion { ref mut name, .. } => { if anonymize != Anonymize::None { - if name != "[anon:mmap]" && name != "[anon:bytehound]" && name != "[anon:glibc]" { - *name = Cow::Borrowed( "" ); + if name != "[anon:mmap]" && name != "[anon:bytehound]" && name != "[anon:glibc]" + { + *name = Cow::Borrowed(""); } } - }, - Event::RemoveRegion { ref mut sources, .. } => { - let mut sources_owned = std::mem::take( sources ).into_owned(); + } + Event::RemoveRegion { + ref mut sources, .. + } => { + let mut sources_owned = std::mem::take(sources).into_owned(); for source in sources_owned.iter_mut() { - if let Some( target_backtrace ) = loader.lookup_backtrace( source.source.backtrace ) { + if let Some(target_backtrace) = loader.lookup_backtrace(source.source.backtrace) + { source.source.backtrace = target_backtrace.raw() as _; } else { source.source.backtrace = u64::MAX; @@ -198,84 +244,85 @@ pub fn postprocess< F, G, D, I >( ifp: F, ofp: G, debug_symbols: I, anonymize: } *sources = sources_owned.into(); - }, - Event::UpdateRegionUsage { .. } => {}, - Event::Header( ref mut body ) => { - anonymize_header( anonymize, body ); - }, + } + Event::UpdateRegionUsage { .. } => {} + Event::Header(ref mut body) => { + anonymize_header(anonymize, body); + } Event::MemoryDump { .. } => { if anonymize != Anonymize::None { write = false; } - }, - Event::Marker { .. } => {}, + } + Event::Marker { .. } => {} Event::Environ { .. } => { if anonymize != Anonymize::None { write = false; } - }, - Event::WallClock { .. } => {}, - Event::String { .. } => {}, - Event::DecodedFrame { .. } => {}, + } + Event::WallClock { .. } => {} + Event::String { .. } => {} + Event::DecodedFrame { .. } => {} Event::DecodedBacktrace { .. } => {} } if write { - event.write_to_stream( &mut ofp )?; + event.write_to_stream(&mut ofp)?; } if is_backtrace { frames.clear(); frames_to_write.clear(); - let backtrace_id = loader.process_backtrace_event( event, |frame_id, is_new| { - frames.push( frame_id as u32 ); + let backtrace_id = loader.process_backtrace_event(event, |frame_id, is_new| { + frames.push(frame_id as u32); if is_new { - frames_to_write.push( frame_id ); + frames_to_write.push(frame_id); } }); if backtrace_id.is_none() { - assert!( frames.is_empty() ); - assert!( frames_to_write.is_empty() ); + assert!(frames.is_empty()); + assert!(frames_to_write.is_empty()); } - for frame_id in frames_to_write.drain( .. ) { - let frame = loader.get_frame( frame_id ).clone(); + for frame_id in frames_to_write.drain(..) { + let frame = loader.get_frame(frame_id).clone(); macro_rules! intern { ($value:expr, $anonymizer:ident) => { - if let Some( id ) = $value { + if let Some(id) = $value { let raw_id = id.to_usize() as u32; - if !emitted_strings.contains( &id ) { - emitted_strings.insert( id ); - let string = loader.interner().resolve( id ).unwrap(); + if !emitted_strings.contains(&id) { + emitted_strings.insert(id); + let string = loader.interner().resolve(id).unwrap(); Event::String { id: raw_id, - string: $anonymizer.anonymize( anonymize, string ) - }.write_to_stream( &mut ofp )?; + string: $anonymizer.anonymize(anonymize, string), + } + .write_to_stream(&mut ofp)?; } raw_id } else { 0xFFFFFFFF } - } + }; } - let library = intern!( frame.library(), anonymizer_library ); - let source = intern!( frame.source(), anonymizer_source ); + let library = intern!(frame.library(), anonymizer_library); + let source = intern!(frame.source(), anonymizer_source); let raw_function; let function; if anonymize == Anonymize::Full { - raw_function = intern!( frame.raw_function(), anonymizer_function ); + raw_function = intern!(frame.raw_function(), anonymizer_function); function = 0xFFFFFFFF; } else { - raw_function = intern!( frame.raw_function(), anonymizer_function ); - function = intern!( frame.function(), anonymizer_function ); + raw_function = intern!(frame.raw_function(), anonymizer_function); + function = intern!(frame.function(), anonymizer_function); } - assert_eq!( frame_id, expected_frame_id ); + assert_eq!(frame_id, expected_frame_id); expected_frame_id += 1; Event::DecodedFrame { @@ -284,22 +331,24 @@ pub fn postprocess< F, G, D, I >( ifp: F, ofp: G, debug_symbols: I, anonymize: raw_function, function, source, - line: frame.line().unwrap_or( 0xFFFFFFFF ), - column: frame.column().unwrap_or( 0xFFFFFFFF ), - is_inline: frame.is_inline() - }.write_to_stream( &mut ofp )?; + line: frame.line().unwrap_or(0xFFFFFFFF), + column: frame.column().unwrap_or(0xFFFFFFFF), + is_inline: frame.is_inline(), + } + .write_to_stream(&mut ofp)?; } - if let Some( backtrace_id ) = backtrace_id { - assert_eq!( backtrace_id.raw(), expected_backtrace_id ); + if let Some(backtrace_id) = backtrace_id { + assert_eq!(backtrace_id.raw(), expected_backtrace_id); expected_backtrace_id += 1; Event::DecodedBacktrace { - frames: (&frames).into() - }.write_to_stream( &mut ofp )?; + frames: (&frames).into(), + } + .write_to_stream(&mut ofp)?; } } else if process { - loader.process( event ); + loader.process(event); } } diff --git a/cli-core/src/reader.rs b/cli-core/src/reader.rs index e6c975c4..0ebc669c 100644 --- a/cli-core/src/reader.rs +++ b/cli-core/src/reader.rs @@ -1,55 +1,61 @@ use std::io::{self, Read}; -use common::event::{ - Event, - HeaderBody -}; +use common::event::{Event, HeaderBody}; -use common::speedy::Readable; use crate::threaded_lz4_stream::Lz4Reader; +use common::speedy::Readable; -pub struct Iter< T: Read + Send > { - fp: Lz4Reader< T >, - done: bool +pub struct Iter { + fp: Lz4Reader, + done: bool, } -impl< T > Iterator for Iter< T > where T: Read + Send { - type Item = io::Result< Event< 'static > >; +impl Iterator for Iter +where + T: Read + Send, +{ + type Item = io::Result>; #[inline] - fn next( &mut self ) -> Option< Self::Item > { + fn next(&mut self) -> Option { if self.done { return None; } - match Event::read_from_stream_unbuffered( &mut self.fp ) { - Ok( event ) => Some( Ok( event ) ), - Err( err ) => { + match Event::read_from_stream_unbuffered(&mut self.fp) { + Ok(event) => Some(Ok(event)), + Err(err) => { self.done = true; let err: io::Error = err.into(); if err.kind() == io::ErrorKind::UnexpectedEof { None } else { - Some( Err( err ) ) + Some(Err(err)) } } } } } -pub fn parse_events< T >( fp: T ) -> io::Result< (HeaderBody, impl Iterator< Item = io::Result< Event< 'static > > >) > where T: Read + Send + 'static { - let mut fp = Lz4Reader::new( fp ); +pub fn parse_events( + fp: T, +) -> io::Result<(HeaderBody, impl Iterator>>)> +where + T: Read + Send + 'static, +{ + let mut fp = Lz4Reader::new(fp); - let event = Event::read_from_stream_unbuffered( &mut fp )?; + let event = Event::read_from_stream_unbuffered(&mut fp)?; let header = match event { - Event::Header( header ) => { - header - }, + Event::Header(header) => header, _ => { - return Err( io::Error::new( io::ErrorKind::Other, "data file doesn't start with a proper header" ) ); + return Err(io::Error::new( + io::ErrorKind::Other, + "data file doesn't start with a proper header", + )); } }; let iter = Iter { fp, done: false }; - Ok( (header, iter) ) + Ok((header, iter)) } diff --git a/cli-core/src/repack.rs b/cli-core/src/repack.rs index c2417f4e..46d9b911 100644 --- a/cli-core/src/repack.rs +++ b/cli-core/src/repack.rs @@ -1,31 +1,30 @@ use std::io::{self, Read, Write}; -use common::speedy::{ - Writable -}; +use common::speedy::Writable; use common::event::Event; use common::lz4_stream::Lz4Writer; use crate::reader::parse_events; -pub fn repack< F, G >( disable_compression: bool, input_fp: F, output_fp: G ) -> Result< (), io::Error > - where F: Read + Send + 'static, - G: Write + Send + 'static +pub fn repack(disable_compression: bool, input_fp: F, output_fp: G) -> Result<(), io::Error> +where + F: Read + Send + 'static, + G: Write + Send + 'static, { - let (header, event_stream) = parse_events( input_fp )?; - let mut output_fp = Lz4Writer::new( output_fp ); + let (header, event_stream) = parse_events(input_fp)?; + let mut output_fp = Lz4Writer::new(output_fp); if disable_compression { output_fp.disable_compression()?; } - Event::Header( header ).write_to_stream( &mut output_fp )?; + Event::Header(header).write_to_stream(&mut output_fp)?; for event in event_stream { let event = event?; - event.write_to_stream( &mut output_fp )?; + event.write_to_stream(&mut output_fp)?; } output_fp.flush()?; Ok(()) -} \ No newline at end of file +} diff --git a/cli-core/src/script.rs b/cli-core/src/script.rs index 6c553f44..cdd9a6e2 100644 --- a/cli-core/src/script.rs +++ b/cli-core/src/script.rs @@ -1,23 +1,26 @@ -use std::cell::Cell; -use std::path::{Path, PathBuf}; -use std::fs::File; -use std::sync::Arc; -use std::sync::atomic::AtomicUsize; -use std::fmt::Write; +use crate::data::OperationId; +use crate::exporter_flamegraph_pl::dump_collation_from_iter; +use crate::filter::{ + AllocationFilter, Compile, Duration, Filter, MapFilter, NumberOrFractionOfTotal, + RawAllocationFilter, RawMapFilter, TryMatch, +}; +use crate::timeline::{build_allocation_timeline, build_map_timeline}; +use crate::{AllocationId, BacktraceId, Data, Loader, MapId, Timestamp, UsageDelta}; use ahash::AHashMap as HashMap; use ahash::AHashSet as HashSet; -use rayon::prelude::*; use parking_lot::Mutex; +use rayon::prelude::*; use regex::Regex; -use crate::{AllocationId, BacktraceId, Data, Loader, MapId, Timestamp, UsageDelta}; -use crate::data::OperationId; -use crate::exporter_flamegraph_pl::dump_collation_from_iter; -use crate::filter::{AllocationFilter, RawAllocationFilter, Duration, Filter, NumberOrFractionOfTotal, Compile, TryMatch, MapFilter, RawMapFilter}; -use crate::timeline::{build_allocation_timeline, build_map_timeline}; +use std::cell::Cell; +use std::fmt::Write; +use std::fs::File; +use std::path::{Path, PathBuf}; +use std::sync::atomic::AtomicUsize; +use std::sync::Arc; -pub use rhai; -pub use crate::script_virtual::VirtualEnvironment; pub use crate::script_virtual::ScriptOutputKind; +pub use crate::script_virtual::VirtualEnvironment; +pub use rhai; struct DecomposedDuration { days: u64, @@ -25,49 +28,49 @@ struct DecomposedDuration { minutes: u64, secs: u64, ms: u64, - us: u64 + us: u64, } impl std::fmt::Display for DecomposedDuration { - fn fmt( &self, fmt: &mut std::fmt::Formatter ) -> std::fmt::Result { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { let mut non_empty = false; if self.days > 0 { non_empty = true; - write!( fmt, "{}d", self.days ).unwrap(); + write!(fmt, "{}d", self.days).unwrap(); } if self.hours > 0 { if non_empty { - fmt.write_str( " " ).unwrap(); + fmt.write_str(" ").unwrap(); } non_empty = true; - write!( fmt, "{}h", self.hours ).unwrap(); + write!(fmt, "{}h", self.hours).unwrap(); } if self.minutes > 0 { if non_empty { - fmt.write_str( " " ).unwrap(); + fmt.write_str(" ").unwrap(); } non_empty = true; - write!( fmt, "{}m", self.minutes ).unwrap(); + write!(fmt, "{}m", self.minutes).unwrap(); } if self.secs > 0 { if non_empty { - fmt.write_str( " " ).unwrap(); + fmt.write_str(" ").unwrap(); } non_empty = true; - write!( fmt, "{}s", self.secs ).unwrap(); + write!(fmt, "{}s", self.secs).unwrap(); } if self.ms > 0 { if non_empty { - fmt.write_str( " " ).unwrap(); + fmt.write_str(" ").unwrap(); } non_empty = true; - write!( fmt, "{}ms", self.ms ).unwrap(); + write!(fmt, "{}ms", self.ms).unwrap(); } if self.us > 0 { if non_empty { - fmt.write_str( " " ).unwrap(); + fmt.write_str(" ").unwrap(); } - write!( fmt, "{}us", self.us ).unwrap(); + write!(fmt, "{}us", self.us).unwrap(); } Ok(()) @@ -75,7 +78,7 @@ impl std::fmt::Display for DecomposedDuration { } impl Duration { - fn decompose( self ) -> DecomposedDuration { + fn decompose(self) -> DecomposedDuration { const MS: u64 = 1000; const SECOND: u64 = 1000 * MS; const MINUTE: u64 = 60 * SECOND; @@ -100,48 +103,44 @@ impl Duration { minutes, secs, ms, - us + us, } } } -fn dirname( path: &str ) -> String { - match std::path::Path::new( path ).parent() { - Some( parent ) => { - parent.to_str().unwrap().into() - }, - None => { - ".".into() - } +fn dirname(path: &str) -> String { + match std::path::Path::new(path).parent() { + Some(parent) => parent.to_str().unwrap().into(), + None => ".".into(), } } #[derive(Clone)] -struct DataRef( Arc< Data > ); +struct DataRef(Arc); impl std::fmt::Debug for DataRef { - fn fmt( &self, fmt: &mut std::fmt::Formatter ) -> std::fmt::Result { - write!( fmt, "Data" ) + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(fmt, "Data") } } impl std::ops::Deref for DataRef { type Target = Data; - fn deref( &self ) -> &Self::Target { + fn deref(&self) -> &Self::Target { &self.0 } } impl DataRef { - fn allocations( &mut self ) -> AllocationList { + fn allocations(&mut self) -> AllocationList { AllocationList { data: self.clone(), allocation_ids: None, - filter: None + filter: None, } } - fn maps( &mut self ) -> MapList { + fn maps(&mut self) -> MapList { MapList { data: self.clone(), map_ids: None, @@ -186,52 +185,54 @@ lazy_static::lazy_static! { pub struct Backtrace { data: DataRef, id: BacktraceId, - strip: bool + strip: bool, } impl Backtrace { - fn write_to( &self, mut fmt: impl std::fmt::Write ) -> std::fmt::Result { + fn write_to(&self, mut fmt: impl std::fmt::Write) -> std::fmt::Result { let mut is_first = true; let interner = self.data.interner(); - for (index, (_, frame)) in self.data.get_backtrace( self.id ).enumerate() { - let function = frame.any_function().map( |function| interner.resolve( function ).unwrap() ); + for (index, (_, frame)) in self.data.get_backtrace(self.id).enumerate() { + let function = frame + .any_function() + .map(|function| interner.resolve(function).unwrap()); if self.strip { - if let Some( function ) = function { - if NOISY_FRAMES.contains( function ) { + if let Some(function) = function { + if NOISY_FRAMES.contains(function) { continue; } } } if !is_first { - write!( fmt, "\n" )?; + write!(fmt, "\n")?; } is_first = false; - write!( fmt, "#{:02}", index )?; - if let Some( library ) = frame.library() { - write!( fmt, " [{}]", interner.resolve( library ).unwrap() )?; + write!(fmt, "#{:02}", index)?; + if let Some(library) = frame.library() { + write!(fmt, " [{}]", interner.resolve(library).unwrap())?; } - if let Some( function ) = function { - write!( fmt, " {}", function )?; + if let Some(function) = function { + write!(fmt, " {}", function)?; } else { - write!( fmt, " {:0x}", frame.address().raw() )?; + write!(fmt, " {:0x}", frame.address().raw())?; } - if let Some( source ) = frame.source() { - let mut source = interner.resolve( source ).unwrap(); - if let Some( index ) = source.rfind( "/" ) { - source = &source[ index + 1.. ]; + if let Some(source) = frame.source() { + let mut source = interner.resolve(source).unwrap(); + if let Some(index) = source.rfind("/") { + source = &source[index + 1..]; } - write!( fmt, " [{}", source )?; - if let Some( line ) = frame.line() { - write!( fmt, ":{}", line )?; + write!(fmt, " [{}", source)?; + if let Some(line) = frame.line() { + write!(fmt, ":{}", line)?; } - write!( fmt, "]" )?; + write!(fmt, "]")?; } if self.strip { - if let Some( function ) = function { - if TERMINAL_FRAMES.contains( function ) { + if let Some(function) = function { + if TERMINAL_FRAMES.contains(function) { break; } } @@ -243,78 +244,76 @@ impl Backtrace { } impl std::fmt::Debug for Backtrace { - fn fmt( &self, fmt: &mut std::fmt::Formatter ) -> std::fmt::Result { - write!( fmt, "Backtrace" ) + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(fmt, "Backtrace") } } impl std::fmt::Display for Backtrace { - fn fmt( &self, fmt: &mut std::fmt::Formatter ) -> std::fmt::Result { - self.write_to( fmt ) + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + self.write_to(fmt) } } #[derive(Clone)] pub struct Allocation { data: DataRef, - id: AllocationId + id: AllocationId, } impl std::fmt::Debug for Allocation { - fn fmt( &self, fmt: &mut std::fmt::Formatter ) -> std::fmt::Result { - write!( fmt, "Allocation" ) + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(fmt, "Allocation") } } #[derive(Clone)] pub struct AllocationList { data: DataRef, - allocation_ids: Option< Arc< Vec< AllocationId > > >, - filter: Option< AllocationFilter > + allocation_ids: Option>>, + filter: Option, } impl std::fmt::Debug for AllocationList { - fn fmt( &self, fmt: &mut std::fmt::Formatter ) -> std::fmt::Result { - write!( fmt, "AllocationList" ) + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(fmt, "AllocationList") } } #[derive(Clone)] pub struct Map { data: DataRef, - id: MapId + id: MapId, } impl std::fmt::Debug for Map { - fn fmt( &self, fmt: &mut std::fmt::Formatter ) -> std::fmt::Result { - write!( fmt, "Map" ) + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(fmt, "Map") } } #[derive(Clone)] pub struct MapList { data: DataRef, - map_ids: Option< Arc< Vec< MapId > > >, - filter: Option< MapFilter > + map_ids: Option>>, + filter: Option, } impl std::fmt::Debug for MapList { - fn fmt( &self, fmt: &mut std::fmt::Formatter ) -> std::fmt::Result { - write!( fmt, "MapList" ) + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(fmt, "MapList") } } // This was copied from the `plotters` crate. -fn gen_keypoints( range: (u64, u64), max_points: usize ) -> Vec< u64 > { +fn gen_keypoints(range: (u64, u64), max_points: usize) -> Vec { let mut scale: u64 = 1; let range = (range.0.min(range.1), range.0.max(range.1)); 'outer: while (range.1 - range.0 + scale - 1) as usize / (scale as usize) > max_points { let next_scale = scale * 10; for new_scale in [scale * 2, scale * 5, scale * 10].iter() { scale = *new_scale; - if (range.1 - range.0 + *new_scale - 1) as usize / (*new_scale as usize) - < max_points - { + if (range.1 - range.0 + *new_scale - 1) as usize / (*new_scale as usize) < max_points { break 'outer; } } @@ -335,62 +334,66 @@ fn gen_keypoints( range: (u64, u64), max_points: usize ) -> Vec< u64 > { return ret; } -fn to_chrono( timestamp: u64 ) -> chrono::DateTime< chrono::Utc > { +fn to_chrono(timestamp: u64) -> chrono::DateTime { use chrono::prelude::*; let secs = timestamp / 1_000_000; - Utc.timestamp( secs as i64, ((timestamp - secs * 1_000_000) * 1000) as u32 ) + Utc.timestamp_opt(secs as i64, ((timestamp - secs * 1_000_000) * 1000) as u32) + .unwrap() } -fn expand_datapoints< V >( xs: &[u64], datapoints: &[(u64, V)] ) -> Vec< (u64, V) > where V: Copy + Default { +fn expand_datapoints(xs: &[u64], datapoints: &[(u64, V)]) -> Vec<(u64, V)> +where + V: Copy + Default, +{ if xs.is_empty() { return Vec::new(); } if datapoints.is_empty() { - return xs.iter().map( |&x| (x, Default::default()) ).collect(); + return xs.iter().map(|&x| (x, Default::default())).collect(); } - assert!( xs.len() >= datapoints.len() ); - assert!( xs[0] <= datapoints[0].0 ); - assert!( xs[xs.len() - 1] >= datapoints[datapoints.len() - 1].0 ); + assert!(xs.len() >= datapoints.len()); + assert!(xs[0] <= datapoints[0].0); + assert!(xs[xs.len() - 1] >= datapoints[datapoints.len() - 1].0); - let mut expanded = Vec::with_capacity( xs.len() ); + let mut expanded = Vec::with_capacity(xs.len()); let mut last_value = Default::default(); let mut dense = xs.iter().copied(); let mut sparse = datapoints.iter().copied(); - while let Some( mut dense_key ) = dense.next() { - if let Some( (sparse_key, value) ) = sparse.next() { + while let Some(mut dense_key) = dense.next() { + if let Some((sparse_key, value)) = sparse.next() { if dense_key < sparse_key { while dense_key < sparse_key { - expanded.push( (dense_key, last_value) ); + expanded.push((dense_key, last_value)); dense_key = dense.next().unwrap(); } } else if dense_key > sparse_key { unreachable!(); } - expanded.push( (dense_key, value) ); + expanded.push((dense_key, value)); last_value = value; } else { - expanded.push( (dense_key, last_value) ); + expanded.push((dense_key, last_value)); } } - assert_eq!( xs.len(), expanded.len() ); + assert_eq!(xs.len(), expanded.len()); expanded } #[test] fn test_expand_datapoints() { assert_eq!( - expand_datapoints( &[0, 1, 2], &[(1, 100)] ), + expand_datapoints(&[0, 1, 2], &[(1, 100)]), &[(0, 0), (1, 100), (2, 100)] ); assert_eq!( - expand_datapoints( &[0, 1, 2, 3], &[(0, 100), (2, 200)] ), + expand_datapoints(&[0, 1, 2, 3], &[(0, 100), (2, 200)]), &[(0, 100), (1, 100), (2, 200), (3, 200)] ); } @@ -399,23 +402,32 @@ fn test_expand_datapoints() { enum OpFilter { Both, OnlyAlloc, - None + None, } -fn get_timestamp( data: &Data, op: OperationId ) -> common::Timestamp { +fn get_timestamp(data: &Data, op: OperationId) -> common::Timestamp { if op.is_allocation() || op.is_reallocation() { - data.get_allocation( op.id() ).timestamp + data.get_allocation(op.id()).timestamp } else { - data.get_allocation( op.id() ).deallocation.as_ref().unwrap().timestamp - } -} - -fn filtered_ids< 'a, T >( list: &'a T ) -> impl ParallelIterator< Item = ::Id > + 'a where T: List + Send + Sync { - let filter = list.filter_ref().map( |filter| filter.compile( list.data_ref() ) ); - list.unfiltered_ids_par_iter().filter( move |&id| { - let allocation = list.get_native_item( id ); - if let Some( ref filter ) = filter { - filter.try_match( list.data_ref(), allocation ) + data.get_allocation(op.id()) + .deallocation + .as_ref() + .unwrap() + .timestamp + } +} + +fn filtered_ids<'a, T>(list: &'a T) -> impl ParallelIterator::Id> + 'a +where + T: List + Send + Sync, +{ + let filter = list + .filter_ref() + .map(|filter| filter.compile(list.data_ref())); + list.unfiltered_ids_par_iter().filter(move |&id| { + let allocation = list.get_native_item(id); + if let Some(ref filter) = filter { + filter.try_match(list.data_ref(), allocation) } else { true } @@ -429,216 +441,235 @@ trait List: Sized + Send + Sync { fn create( data: DataRef, - unfiltered_ids: Option< Arc< Vec< Self::Id > > >, - filter: Option< Filter< Self::RawFilter > > + unfiltered_ids: Option>>, + filter: Option>, ) -> Self; - fn create_item( &self, id: Self::Id ) -> Self::Item; - fn data_ref( &self ) -> &DataRef; - fn filter_ref( &self ) -> Option< &Filter< Self::RawFilter > >; - fn unfiltered_ids_ref( &self ) -> Option< &Arc< Vec< Self::Id > > >; - fn get_native_item( &self, id: Self::Id ) -> &<::Compiled as TryMatch>::Item; - fn default_unfiltered_ids( data: &Data ) -> &[Self::Id]; - fn list_by_backtrace( data: &Data, backtrace: BacktraceId ) -> Vec< Self::Id >; - - fn unfiltered_ids( &self ) -> &[Self::Id] { - self.unfiltered_ids_ref().map( |map_ids| map_ids.as_slice() ).unwrap_or_else( || Self::default_unfiltered_ids( &self.data_ref() ) ) - } - - fn unfiltered_ids_iter( &self ) -> std::iter::Copied< std::slice::Iter< Self::Id > > { + fn create_item(&self, id: Self::Id) -> Self::Item; + fn data_ref(&self) -> &DataRef; + fn filter_ref(&self) -> Option<&Filter>; + fn unfiltered_ids_ref(&self) -> Option<&Arc>>; + fn get_native_item( + &self, + id: Self::Id, + ) -> &<::Compiled as TryMatch>::Item; + fn default_unfiltered_ids(data: &Data) -> &[Self::Id]; + fn list_by_backtrace(data: &Data, backtrace: BacktraceId) -> Vec; + + fn unfiltered_ids(&self) -> &[Self::Id] { + self.unfiltered_ids_ref() + .map(|map_ids| map_ids.as_slice()) + .unwrap_or_else(|| Self::default_unfiltered_ids(&self.data_ref())) + } + + fn unfiltered_ids_iter(&self) -> std::iter::Copied> { self.unfiltered_ids().iter().copied() } - fn unfiltered_ids_par_iter( &self ) -> rayon::iter::Copied< rayon::slice::Iter< Self::Id > > { + fn unfiltered_ids_par_iter(&self) -> rayon::iter::Copied> { self.unfiltered_ids().par_iter().copied() } - fn len( &mut self ) -> i64 { + fn len(&mut self) -> i64 { self.apply_filter(); self.unfiltered_ids().len() as i64 } - fn apply_filter( &mut self ) { + fn apply_filter(&mut self) { if self.filter_ref().is_none() { return; } - let list: Vec< _ > = filtered_ids( self ).collect(); - *self = Self::create( - self.data_ref().clone(), - Some( Arc::new( list ) ), - None - ); + let list: Vec<_> = filtered_ids(self).collect(); + *self = Self::create(self.data_ref().clone(), Some(Arc::new(list)), None); } - fn clone_with_filter( &self, filter: Option< Filter< Self::RawFilter > > ) -> Self { - Self::create( self.data_ref().clone(), self.unfiltered_ids_ref().cloned(), filter ) + fn clone_with_filter(&self, filter: Option>) -> Self { + Self::create( + self.data_ref().clone(), + self.unfiltered_ids_ref().cloned(), + filter, + ) } - fn uses_same_list( &self, rhs: &Self ) -> bool { + fn uses_same_list(&self, rhs: &Self) -> bool { match (self.unfiltered_ids_ref(), rhs.unfiltered_ids_ref()) { (None, None) => true, - (Some( lhs ), Some( rhs )) => { - Arc::ptr_eq( lhs, rhs ) - }, - _ => false + (Some(lhs), Some(rhs)) => Arc::ptr_eq(lhs, rhs), + _ => false, } } - fn add_filter( &self, callback: impl FnOnce( &mut Self::RawFilter ) ) -> Self { - self.add_filter_once( |_| false, callback ) + fn add_filter(&self, callback: impl FnOnce(&mut Self::RawFilter)) -> Self { + self.add_filter_once(|_| false, callback) } - fn add_filter_once( &self, is_filled: impl FnOnce( &Self::RawFilter ) -> bool, callback: impl FnOnce( &mut Self::RawFilter ) ) -> Self { + fn add_filter_once( + &self, + is_filled: impl FnOnce(&Self::RawFilter) -> bool, + callback: impl FnOnce(&mut Self::RawFilter), + ) -> Self { let filter = match self.filter_ref() { None => { let mut new_filter = Self::RawFilter::default(); - callback( &mut new_filter ); + callback(&mut new_filter); - Filter::Basic( new_filter ) - }, - Some( Filter::Basic( ref old_filter ) ) => { - if is_filled( old_filter ) { + Filter::Basic(new_filter) + } + Some(Filter::Basic(ref old_filter)) => { + if is_filled(old_filter) { let mut new_filter = Self::RawFilter::default(); - callback( &mut new_filter ); + callback(&mut new_filter); - Filter::And( Box::new( Filter::Basic( old_filter.clone() ) ), Box::new( Filter::Basic( new_filter ) ) ) + Filter::And( + Box::new(Filter::Basic(old_filter.clone())), + Box::new(Filter::Basic(new_filter)), + ) } else { let mut new_filter = old_filter.clone(); - callback( &mut new_filter ); + callback(&mut new_filter); - Filter::Basic( new_filter ) + Filter::Basic(new_filter) } - }, - Some( Filter::And( ref lhs, ref rhs ) ) if matches!( **rhs, Filter::Basic( _ ) ) => { - match **rhs { - Filter::Basic( ref old_filter ) => { - let mut new_filter = old_filter.clone(); - callback( &mut new_filter ); + } + Some(Filter::And(ref lhs, ref rhs)) if matches!(**rhs, Filter::Basic(_)) => match **rhs + { + Filter::Basic(ref old_filter) => { + let mut new_filter = old_filter.clone(); + callback(&mut new_filter); - Filter::And( lhs.clone(), Box::new( Filter::Basic( new_filter ) ) ) - }, - _ => unreachable!() + Filter::And(lhs.clone(), Box::new(Filter::Basic(new_filter))) } + _ => unreachable!(), }, - Some( old_filter ) => { + Some(old_filter) => { let mut new_filter = Self::RawFilter::default(); - callback( &mut new_filter ); + callback(&mut new_filter); - Filter::And( Box::new( old_filter.clone() ), Box::new( Filter::Basic( new_filter ) ) ) + Filter::And( + Box::new(old_filter.clone()), + Box::new(Filter::Basic(new_filter)), + ) } }; - self.clone_with_filter( Some( filter ) ) + self.clone_with_filter(Some(filter)) } - fn rhai_merge( mut lhs: Self, mut rhs: Self ) -> Result< Self, Box< rhai::EvalAltResult > > { + fn rhai_merge(mut lhs: Self, mut rhs: Self) -> Result> { if lhs.data_ref().id != rhs.data_ref().id { - return Err( Box::new( rhai::EvalAltResult::from( "lists don't come from the same data file" ) ) ); + return Err(Box::new(rhai::EvalAltResult::from( + "lists don't come from the same data file", + ))); } - if lhs.uses_same_list( &rhs ) { + if lhs.uses_same_list(&rhs) { let filter = match (lhs.filter_ref(), rhs.filter_ref()) { - (Some( lhs ), Some( rhs )) => Some( Filter::Or( Box::new( lhs.clone() ), Box::new( rhs.clone() ) ) ), - _ => None + (Some(lhs), Some(rhs)) => { + Some(Filter::Or(Box::new(lhs.clone()), Box::new(rhs.clone()))) + } + _ => None, }; - Ok( lhs.clone_with_filter( filter ) ) + Ok(lhs.clone_with_filter(filter)) } else { lhs.apply_filter(); rhs.apply_filter(); - let mut set: HashSet< Self::Id > = HashSet::new(); - set.extend( lhs.unfiltered_ids_iter() ); - set.extend( rhs.unfiltered_ids_iter() ); + let mut set: HashSet = HashSet::new(); + set.extend(lhs.unfiltered_ids_iter()); + set.extend(rhs.unfiltered_ids_iter()); - let ids: Vec< _ > = Self::create( lhs.data_ref().clone(), None, None ).unfiltered_ids_par_iter().filter( |id| set.contains( &id ) ).collect(); - Ok( Self::create( + let ids: Vec<_> = Self::create(lhs.data_ref().clone(), None, None) + .unfiltered_ids_par_iter() + .filter(|id| set.contains(&id)) + .collect(); + Ok(Self::create( lhs.data_ref().clone(), - Some( Arc::new( ids ) ), - None + Some(Arc::new(ids)), + None, )) } } - fn rhai_substract( lhs: Self, mut rhs: Self ) -> Result< Self, Box< rhai::EvalAltResult > > { + fn rhai_substract(lhs: Self, mut rhs: Self) -> Result> { if lhs.data_ref().id != rhs.data_ref().id { - return Err( Box::new( rhai::EvalAltResult::from( "lists don't come from the same data file" ) ) ); + return Err(Box::new(rhai::EvalAltResult::from( + "lists don't come from the same data file", + ))); } - if lhs.uses_same_list( &rhs ) { + if lhs.uses_same_list(&rhs) { let filter = match (lhs.filter_ref(), rhs.filter_ref()) { (_, None) => { - return Ok( Self::create( + return Ok(Self::create( lhs.data_ref().clone(), - Some( Arc::new( Vec::new() ) ), - None + Some(Arc::new(Vec::new())), + None, )); - }, - (None, Some( rhs )) => Some( - Filter::Not( - Box::new( rhs.clone() ) - ) - ), - (Some( lhs ), Some( rhs )) => Some( - Filter::And( - Box::new( lhs.clone() ), - Box::new( Filter::Not( - Box::new( rhs.clone() ) - )) - ) - ) + } + (None, Some(rhs)) => Some(Filter::Not(Box::new(rhs.clone()))), + (Some(lhs), Some(rhs)) => Some(Filter::And( + Box::new(lhs.clone()), + Box::new(Filter::Not(Box::new(rhs.clone()))), + )), }; - Ok( lhs.clone_with_filter( filter ) ) + Ok(lhs.clone_with_filter(filter)) } else { rhs.apply_filter(); - let mut set: HashSet< Self::Id > = HashSet::new(); - set.extend( rhs.unfiltered_ids_iter() ); + let mut set: HashSet = HashSet::new(); + set.extend(rhs.unfiltered_ids_iter()); - let ids: Vec< _ > = filtered_ids( &lhs ).filter( |id| !set.contains( id ) ).collect(); - Ok( Self::create( + let ids: Vec<_> = filtered_ids(&lhs).filter(|id| !set.contains(id)).collect(); + Ok(Self::create( lhs.data_ref().clone(), - Some( Arc::new( ids ) ), - None + Some(Arc::new(ids)), + None, )) } } - fn rhai_intersect( lhs: Self, mut rhs: Self ) -> Result< Self, Box< rhai::EvalAltResult > > { + fn rhai_intersect(lhs: Self, mut rhs: Self) -> Result> { if lhs.data_ref().id != rhs.data_ref().id { - return Err( Box::new( rhai::EvalAltResult::from( "lists don't come from the same data file" ) ) ); + return Err(Box::new(rhai::EvalAltResult::from( + "lists don't come from the same data file", + ))); } - if lhs.uses_same_list( &rhs ) { + if lhs.uses_same_list(&rhs) { let filter = match (lhs.filter_ref(), rhs.filter_ref()) { (None, None) => None, - (Some( lhs ), None) => Some( lhs.clone() ), - (None, Some( rhs )) => Some( rhs.clone() ), - (Some( lhs ), Some( rhs )) => Some( Filter::And( Box::new( lhs.clone() ), Box::new( rhs.clone() ) ) ) + (Some(lhs), None) => Some(lhs.clone()), + (None, Some(rhs)) => Some(rhs.clone()), + (Some(lhs), Some(rhs)) => { + Some(Filter::And(Box::new(lhs.clone()), Box::new(rhs.clone()))) + } }; - Ok( lhs.clone_with_filter( filter ) ) + Ok(lhs.clone_with_filter(filter)) } else { rhs.apply_filter(); - let mut set: HashSet< Self::Id > = HashSet::new(); - set.extend( rhs.unfiltered_ids_iter() ); + let mut set: HashSet = HashSet::new(); + set.extend(rhs.unfiltered_ids_iter()); - let ids: Vec< _ > = filtered_ids( &lhs ).filter( |id| set.contains( id ) ).collect(); - Ok( Self::create( + let ids: Vec<_> = filtered_ids(&lhs).filter(|id| set.contains(id)).collect(); + Ok(Self::create( lhs.data_ref().clone(), - Some( Arc::new( ids ) ), - None + Some(Arc::new(ids)), + None, )) } } - fn rhai_get( &mut self, index: i64 ) -> Result< Self::Item, Box< rhai::EvalAltResult > > { + fn rhai_get(&mut self, index: i64) -> Result> { self.apply_filter(); let list = self.unfiltered_ids(); - let id = list.get( index as usize ).ok_or_else( || error( "index out of range" ) )?; - Ok( self.create_item( *id ) ) + let id = list + .get(index as usize) + .ok_or_else(|| error("index out of range"))?; + Ok(self.create_item(*id)) } } @@ -649,46 +680,58 @@ impl List for MapList { fn create( data: DataRef, - unfiltered_ids: Option< Arc< Vec< Self::Id > > >, - filter: Option< Filter< Self::RawFilter > > + unfiltered_ids: Option>>, + filter: Option>, ) -> Self { MapList { data, map_ids: unfiltered_ids, - filter + filter, } } - fn create_item( &self, id: Self::Id ) -> Self::Item { + fn create_item(&self, id: Self::Id) -> Self::Item { Map { data: self.data.clone(), - id + id, } } - fn data_ref( &self ) -> &DataRef { + fn data_ref(&self) -> &DataRef { &self.data } - fn filter_ref( &self ) -> Option< &Filter< Self::RawFilter > > { + fn filter_ref(&self) -> Option<&Filter> { self.filter.as_ref() } - fn unfiltered_ids_ref( &self ) -> Option< &Arc< Vec< Self::Id > > > { + fn unfiltered_ids_ref(&self) -> Option<&Arc>> { self.map_ids.as_ref() } - fn get_native_item( &self, id: Self::Id ) -> &<::Compiled as TryMatch>::Item { - &self.data.0.maps()[ id.0 as usize ] + fn get_native_item( + &self, + id: Self::Id, + ) -> &<::Compiled as TryMatch>::Item { + &self.data.0.maps()[id.0 as usize] } - fn default_unfiltered_ids( data: &Data ) -> &[Self::Id] { + fn default_unfiltered_ids(data: &Data) -> &[Self::Id] { &data.map_ids } - fn list_by_backtrace( data: &Data, backtrace: BacktraceId ) -> Vec< Self::Id > { + fn list_by_backtrace(data: &Data, backtrace: BacktraceId) -> Vec { // TODO: Cache this. - data.maps().iter().enumerate().filter( |(_, map)| map.source.map( |source| source.backtrace == backtrace ).unwrap_or( false ) ).map( |(index, _)| MapId( index as u64 ) ).collect() + data.maps() + .iter() + .enumerate() + .filter(|(_, map)| { + map.source + .map(|source| source.backtrace == backtrace) + .unwrap_or(false) + }) + .map(|(index, _)| MapId(index as u64)) + .collect() } } @@ -699,139 +742,159 @@ impl List for AllocationList { fn create( data: DataRef, - unfiltered_ids: Option< Arc< Vec< Self::Id > > >, - filter: Option< Filter< Self::RawFilter > > + unfiltered_ids: Option>>, + filter: Option>, ) -> Self { AllocationList { data, allocation_ids: unfiltered_ids, - filter + filter, } } - fn create_item( &self, id: Self::Id ) -> Self::Item { + fn create_item(&self, id: Self::Id) -> Self::Item { Allocation { data: self.data.clone(), - id + id, } } - fn data_ref( &self ) -> &DataRef { + fn data_ref(&self) -> &DataRef { &self.data } - fn filter_ref( &self ) -> Option< &Filter< Self::RawFilter > > { + fn filter_ref(&self) -> Option<&Filter> { self.filter.as_ref() } - fn unfiltered_ids_ref( &self ) -> Option< &Arc< Vec< Self::Id > > > { + fn unfiltered_ids_ref(&self) -> Option<&Arc>> { self.allocation_ids.as_ref() } - fn get_native_item( &self, id: Self::Id ) -> &<::Compiled as TryMatch>::Item { - self.data.get_allocation( id ) + fn get_native_item( + &self, + id: Self::Id, + ) -> &<::Compiled as TryMatch>::Item { + self.data.get_allocation(id) } - fn default_unfiltered_ids( data: &Data ) -> &[Self::Id] { + fn default_unfiltered_ids(data: &Data) -> &[Self::Id] { &data.sorted_by_timestamp } - fn list_by_backtrace( data: &Data, id: BacktraceId ) -> Vec< Self::Id > { - data.get_allocation_ids_by_backtrace( id ).to_owned() + fn list_by_backtrace(data: &Data, id: BacktraceId) -> Vec { + data.get_allocation_ids_by_backtrace(id).to_owned() } } impl AllocationList { - pub fn allocation_ids( &mut self ) -> &[AllocationId] { + pub fn allocation_ids(&mut self) -> &[AllocationId] { self.apply_filter(); self.unfiltered_ids() } - fn save_as_flamegraph_to_string( &mut self ) -> Result< String, Box< rhai::EvalAltResult > > { + fn save_as_flamegraph_to_string(&mut self) -> Result> { self.apply_filter(); let mut lines = Vec::new(); - let iter = self.unfiltered_ids_iter().map( |allocation_id| { - (allocation_id, self.data.get_allocation( allocation_id ) ) - }); + let iter = self + .unfiltered_ids_iter() + .map(|allocation_id| (allocation_id, self.data.get_allocation(allocation_id))); - dump_collation_from_iter( &self.data, iter, |line| { - lines.push( line.to_owned() ); - let result: Result< (), () > = Ok(()); + dump_collation_from_iter(&self.data, iter, |line| { + lines.push(line.to_owned()); + let result: Result<(), ()> = Ok(()); result - }).map_err( |_| Box::new( rhai::EvalAltResult::from( "failed to collate allocations" ) ) )?; + }) + .map_err(|_| Box::new(rhai::EvalAltResult::from("failed to collate allocations")))?; lines.sort_unstable(); let mut output = String::new(); - crate::exporter_flamegraph::lines_to_svg( lines, &mut output ); + crate::exporter_flamegraph::lines_to_svg(lines, &mut output); - Ok( output ) + Ok(output) } - fn save_as_flamegraph( &mut self, env: &mut dyn Environment, path: String ) -> Result< Self, Box< rhai::EvalAltResult > > { + fn save_as_flamegraph( + &mut self, + env: &mut dyn Environment, + path: String, + ) -> Result> { let data = self.save_as_flamegraph_to_string()?; - env.file_write( &path, FileKind::Svg, data.as_bytes() )?; - Ok( self.clone() ) + env.file_write(&path, FileKind::Svg, data.as_bytes())?; + Ok(self.clone()) } - fn save_as_graph( &self, env: &mut dyn Environment, path: String ) -> Result< Self, Box< rhai::EvalAltResult > > { - Graph::new().add( self.clone() )?.save( env, path )?; - Ok( self.clone() ) + fn save_as_graph( + &self, + env: &mut dyn Environment, + path: String, + ) -> Result> { + Graph::new().add(self.clone())?.save(env, path)?; + Ok(self.clone()) } - fn filtered_ops( &mut self, mut callback: impl FnMut( AllocationId ) -> OpFilter ) -> Vec< OperationId > { + fn filtered_ops( + &mut self, + mut callback: impl FnMut(AllocationId) -> OpFilter, + ) -> Vec { self.apply_filter(); let ids = self.unfiltered_ids_iter(); - let mut ops = Vec::with_capacity( ids.len() ); + let mut ops = Vec::with_capacity(ids.len()); for id in ids { - let filter = callback( id ); + let filter = callback(id); if filter == OpFilter::None { continue; } - let allocation = self.data.get_allocation( id ); - ops.push( OperationId::new_allocation( id ) ); + let allocation = self.data.get_allocation(id); + ops.push(OperationId::new_allocation(id)); if allocation.deallocation.is_some() && filter != OpFilter::OnlyAlloc { - ops.push( OperationId::new_deallocation( id ) ); + ops.push(OperationId::new_deallocation(id)); } } - ops.par_sort_by_key( |op| get_timestamp( &self.data, *op ) ); + ops.par_sort_by_key(|op| get_timestamp(&self.data, *op)); ops } - fn group_by_backtrace( &mut self ) -> AllocationGroupList { + fn group_by_backtrace(&mut self) -> AllocationGroupList { #[derive(Default)] struct Group { - allocation_ids: Vec< AllocationId >, - size: u64 + allocation_ids: Vec, + size: u64, } self.apply_filter(); let mut groups = HashMap::new(); for id in self.unfiltered_ids_iter() { - let allocation = self.data.get_allocation( id ); - let group = groups.entry( allocation.backtrace ).or_insert_with( || Group::default() ); + let allocation = self.data.get_allocation(id); + let group = groups + .entry(allocation.backtrace) + .or_insert_with(|| Group::default()); group.size += allocation.size; - group.allocation_ids.push( id ); + group.allocation_ids.push(id); } AllocationGroupList { data: self.data.clone(), - groups: Arc::new( groups.into_iter().map( |(_, group)| { - AllocationGroupInner { - allocation_ids: Arc::new( group.allocation_ids ), - size: group.size - } - }).collect() ) + groups: Arc::new( + groups + .into_iter() + .map(|(_, group)| AllocationGroupInner { + allocation_ids: Arc::new(group.allocation_ids), + size: group.size, + }) + .collect(), + ), } } } impl MapList { - pub fn map_ids( &mut self ) -> &[MapId] { + pub fn map_ids(&mut self) -> &[MapId] { self.apply_filter(); self.unfiltered_ids() } @@ -839,25 +902,25 @@ impl MapList { #[derive(Clone)] struct AllocationGroupInner { - allocation_ids: Arc< Vec< AllocationId > >, - size: u64 + allocation_ids: Arc>, + size: u64, } struct AllocationGroupListIter { group_list: AllocationGroupList, - index: usize + index: usize, } impl Iterator for AllocationGroupListIter { type Item = AllocationList; - fn next( &mut self ) -> Option< Self::Item > { - let group = self.group_list.groups.get( self.index )?; + fn next(&mut self) -> Option { + let group = self.group_list.groups.get(self.index)?; self.index += 1; - Some( AllocationList { + Some(AllocationList { data: self.group_list.data.clone(), - allocation_ids: Some( group.allocation_ids.clone() ), - filter: None + allocation_ids: Some(group.allocation_ids.clone()), + filter: None, }) } } @@ -865,12 +928,12 @@ impl Iterator for AllocationGroupListIter { #[derive(Clone)] struct AllocationGroupList { data: DataRef, - groups: Arc< Vec< AllocationGroupInner > > + groups: Arc>, } impl std::fmt::Debug for AllocationGroupList { - fn fmt( &self, fmt: &mut std::fmt::Formatter ) -> std::fmt::Result { - write!( fmt, "AllocationGroupList" ) + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(fmt, "AllocationGroupList") } } @@ -878,100 +941,110 @@ impl IntoIterator for AllocationGroupList { type Item = AllocationList; type IntoIter = AllocationGroupListIter; - fn into_iter( self ) -> Self::IntoIter { + fn into_iter(self) -> Self::IntoIter { AllocationGroupListIter { group_list: self, - index: 0 + index: 0, } } } impl AllocationGroupList { - fn filter( &self, callback: impl Fn( &AllocationGroupInner ) -> bool + Send + Sync ) -> Self { - let groups: Vec< _ > = self.groups.par_iter() - .filter( |group| callback( group ) ) - .map( |group| group.clone() ) + fn filter(&self, callback: impl Fn(&AllocationGroupInner) -> bool + Send + Sync) -> Self { + let groups: Vec<_> = self + .groups + .par_iter() + .filter(|group| callback(group)) + .map(|group| group.clone()) .collect(); Self { data: self.data.clone(), - groups: Arc::new( groups.into_iter().collect() ) + groups: Arc::new(groups.into_iter().collect()), } } - fn sort_by_key< T >( &self, callback: impl Fn( &AllocationGroupInner ) -> T + Send + Sync ) -> Self where T: Ord { + fn sort_by_key(&self, callback: impl Fn(&AllocationGroupInner) -> T + Send + Sync) -> Self + where + T: Ord, + { let mut groups = (*self.groups).clone(); - groups.par_sort_by_key( callback ); + groups.par_sort_by_key(callback); AllocationGroupList { data: self.data.clone(), - groups: Arc::new( groups ) + groups: Arc::new(groups), } } - fn len( &mut self ) -> i64 { + fn len(&mut self) -> i64 { self.groups.len() as i64 } - fn only_all_leaked( &mut self ) -> AllocationGroupList { - self.filter( |group| group.allocation_ids.par_iter().all( |&id| { - let allocation = self.data.get_allocation( id ); - allocation.deallocation.is_none() - })) + fn only_all_leaked(&mut self) -> AllocationGroupList { + self.filter(|group| { + group.allocation_ids.par_iter().all(|&id| { + let allocation = self.data.get_allocation(id); + allocation.deallocation.is_none() + }) + }) } - fn only_count_at_least( &mut self, count: i64 ) -> AllocationGroupList { - self.filter( |group| group.allocation_ids.len() as i64 >= count ) + fn only_count_at_least(&mut self, count: i64) -> AllocationGroupList { + self.filter(|group| group.allocation_ids.len() as i64 >= count) } - fn sort_by_size_ascending( &mut self ) -> AllocationGroupList { - self.sort_by_key( |group| group.size ) + fn sort_by_size_ascending(&mut self) -> AllocationGroupList { + self.sort_by_key(|group| group.size) } - fn sort_by_size_descending( &mut self ) -> AllocationGroupList { - self.sort_by_key( |group| !group.size ) + fn sort_by_size_descending(&mut self) -> AllocationGroupList { + self.sort_by_key(|group| !group.size) } - fn sort_by_count_ascending( &mut self ) -> AllocationGroupList { - self.sort_by_key( |group| group.allocation_ids.len() ) + fn sort_by_count_ascending(&mut self) -> AllocationGroupList { + self.sort_by_key(|group| group.allocation_ids.len()) } - fn sort_by_count_descending( &mut self ) -> AllocationGroupList { - self.sort_by_key( |group| !group.allocation_ids.len() ) + fn sort_by_count_descending(&mut self) -> AllocationGroupList { + self.sort_by_key(|group| !group.allocation_ids.len()) } - fn ungroup( &mut self ) -> AllocationList { + fn ungroup(&mut self) -> AllocationList { let mut allocation_ids = Vec::new(); for group in &*self.groups { - allocation_ids.extend_from_slice( &group.allocation_ids ); + allocation_ids.extend_from_slice(&group.allocation_ids); } - allocation_ids.par_sort_by_key( |&id| { - let allocation = self.data.get_allocation( id ); + allocation_ids.par_sort_by_key(|&id| { + let allocation = self.data.get_allocation(id); (allocation.timestamp, id) }); AllocationList { data: self.data.clone(), - allocation_ids: Some( Arc::new( allocation_ids ) ), - filter: None + allocation_ids: Some(Arc::new(allocation_ids)), + filter: None, } } - fn get( &mut self, index: i64 ) -> Result< AllocationList, Box< rhai::EvalAltResult > > { - let group = self.groups.get( index as usize ).ok_or_else( || error( "index out of range" ) )?; - Ok( AllocationList { + fn get(&mut self, index: i64) -> Result> { + let group = self + .groups + .get(index as usize) + .ok_or_else(|| error("index out of range"))?; + Ok(AllocationList { data: self.data.clone(), - allocation_ids: Some( group.allocation_ids.clone() ), - filter: None + allocation_ids: Some(group.allocation_ids.clone()), + filter: None, }) } - fn take( &mut self, count: i64 ) -> Self { - let length = std::cmp::min( self.groups.len(), count as usize ); + fn take(&mut self, count: i64) -> Self { + let length = std::cmp::min(self.groups.len(), count as usize); AllocationGroupList { data: self.data.clone(), - groups: Arc::new( self.groups[ ..length ].to_owned() ) + groups: Arc::new(self.groups[..length].to_owned()), } } } @@ -981,28 +1054,28 @@ enum AllocationGraphKind { MemoryUsage, LiveAllocations, NewAllocations, - Deallocations + Deallocations, } #[derive(Copy, Clone)] enum MapGraphKind { RSS, - AddressSpace + AddressSpace, } #[derive(Copy, Clone)] enum GraphKind { - Allocation( AllocationGraphKind ), - Map( MapGraphKind ) + Allocation(AllocationGraphKind), + Map(MapGraphKind), } impl GraphKind { - fn is_for_allocations( &self ) -> bool { - matches!( self, GraphKind::Allocation( .. ) ) + fn is_for_allocations(&self) -> bool { + matches!(self, GraphKind::Allocation(..)) } - fn is_for_maps( &self ) -> bool { - matches!( self, GraphKind::Map( .. ) ) + fn is_for_maps(&self) -> bool { + matches!(self, GraphKind::Map(..)) } } @@ -1014,25 +1087,28 @@ struct Graph { hide_empty: bool, trim_left: bool, trim_right: bool, - start_at: Option< Duration >, - end_at: Option< Duration >, - allocation_lists: Vec< AllocationList >, - map_lists: Vec< MapList >, - labels: Vec< Option< String > >, - gradient: Option< Arc< colorgrad::Gradient > >, - kind: Option< GraphKind >, + start_at: Option, + end_at: Option, + allocation_lists: Vec, + map_lists: Vec, + labels: Vec>, + gradient: Option>, + kind: Option, - cached_datapoints: Option< Arc< (Vec< u64 >, Vec< Vec< (u64, u64) > >) > > + cached_datapoints: Option, Vec>)>>, } -fn finalize_datapoints( mut xs: Vec< u64 >, mut datapoints_for_ops: Vec< Vec< (u64, u64) > > ) -> (Vec< u64 >, Vec< Vec< (u64, u64) > >) { +fn finalize_datapoints( + mut xs: Vec, + mut datapoints_for_ops: Vec>, +) -> (Vec, Vec>) { xs.sort_unstable(); for datapoints in &mut datapoints_for_ops { if datapoints.is_empty() { continue; } - *datapoints = expand_datapoints( &xs, &datapoints ); + *datapoints = expand_datapoints(&xs, &datapoints); } for index in 0..xs.len() { @@ -1042,70 +1118,104 @@ fn finalize_datapoints( mut xs: Vec< u64 >, mut datapoints_for_ops: Vec< Vec< (u continue; } - value += datapoints[ index ].1; - datapoints[ index ].1 = value; + value += datapoints[index].1; + datapoints[index].1 = value; } } (xs, datapoints_for_ops) } -fn prepare_allocation_graph_datapoints( data: &Data, ops_for_list: &[Vec< OperationId >], kind: AllocationGraphKind ) -> (Vec< u64 >, Vec< Vec< (u64, u64) > >) { - let timestamp_min = ops_for_list.iter().flat_map( |ops| ops.first() ).map( |op| get_timestamp( &data, *op ) ).min().unwrap_or( common::Timestamp::min() ); - let timestamp_max = ops_for_list.iter().flat_map( |ops| ops.last() ).map( |op| get_timestamp( &data, *op ) ).max().unwrap_or( common::Timestamp::min() ); +fn prepare_allocation_graph_datapoints( + data: &Data, + ops_for_list: &[Vec], + kind: AllocationGraphKind, +) -> (Vec, Vec>) { + let timestamp_min = ops_for_list + .iter() + .flat_map(|ops| ops.first()) + .map(|op| get_timestamp(&data, *op)) + .min() + .unwrap_or(common::Timestamp::min()); + let timestamp_max = ops_for_list + .iter() + .flat_map(|ops| ops.last()) + .map(|op| get_timestamp(&data, *op)) + .max() + .unwrap_or(common::Timestamp::min()); let mut xs = HashSet::new(); let mut datapoints_for_ops = Vec::new(); for ops in ops_for_list { if ops.is_empty() { - datapoints_for_ops.push( Vec::new() ); + datapoints_for_ops.push(Vec::new()); continue; } - let datapoints: Vec< _ > = build_allocation_timeline( &data, timestamp_min, timestamp_max, ops ).into_iter().map( |point| { - xs.insert( point.timestamp ); - let x = point.timestamp; - let y = match kind { - AllocationGraphKind::MemoryUsage => point.memory_usage, - AllocationGraphKind::LiveAllocations => point.allocations, - AllocationGraphKind::NewAllocations => point.positive_change.allocations, - AllocationGraphKind::Deallocations => point.negative_change.allocations - } as u64; - (x, y) - }).collect(); + let datapoints: Vec<_> = + build_allocation_timeline(&data, timestamp_min, timestamp_max, ops) + .into_iter() + .map(|point| { + xs.insert(point.timestamp); + let x = point.timestamp; + let y = match kind { + AllocationGraphKind::MemoryUsage => point.memory_usage, + AllocationGraphKind::LiveAllocations => point.allocations, + AllocationGraphKind::NewAllocations => point.positive_change.allocations, + AllocationGraphKind::Deallocations => point.negative_change.allocations, + } as u64; + (x, y) + }) + .collect(); - datapoints_for_ops.push( datapoints ); + datapoints_for_ops.push(datapoints); } - finalize_datapoints( xs.into_iter().collect(), datapoints_for_ops ) + finalize_datapoints(xs.into_iter().collect(), datapoints_for_ops) } -fn prepare_map_graph_datapoints( ops_for_list: &[Vec< (Timestamp, UsageDelta) >], kind: MapGraphKind ) -> (Vec< u64 >, Vec< Vec< (u64, u64) > >) { - let timestamp_min = ops_for_list.iter().flat_map( |ops| ops.first() ).map( |(timestamp, _)| *timestamp ).min().unwrap_or( common::Timestamp::min() ); - let timestamp_max = ops_for_list.iter().flat_map( |ops| ops.last() ).map( |(timestamp, _)| *timestamp ).max().unwrap_or( common::Timestamp::min() ); +fn prepare_map_graph_datapoints( + ops_for_list: &[Vec<(Timestamp, UsageDelta)>], + kind: MapGraphKind, +) -> (Vec, Vec>) { + let timestamp_min = ops_for_list + .iter() + .flat_map(|ops| ops.first()) + .map(|(timestamp, _)| *timestamp) + .min() + .unwrap_or(common::Timestamp::min()); + let timestamp_max = ops_for_list + .iter() + .flat_map(|ops| ops.last()) + .map(|(timestamp, _)| *timestamp) + .max() + .unwrap_or(common::Timestamp::min()); let mut xs = HashSet::new(); let mut datapoints_for_ops = Vec::new(); for ops in ops_for_list { if ops.is_empty() { - datapoints_for_ops.push( Vec::new() ); + datapoints_for_ops.push(Vec::new()); continue; } - let datapoints: Vec< _ > = build_map_timeline( timestamp_min, timestamp_max, ops ).into_iter().map( |point| { - xs.insert( point.timestamp ); - let x = point.timestamp; - let y = match kind { - MapGraphKind::RSS => point.rss(), - MapGraphKind::AddressSpace => point.address_space, - } as u64; - (x, y) - }).collect(); + let datapoints: Vec<_> = build_map_timeline(timestamp_min, timestamp_max, ops) + .into_iter() + .map(|point| { + xs.insert(point.timestamp); + let x = point.timestamp; + let y = match kind { + MapGraphKind::RSS => point.rss(), + MapGraphKind::AddressSpace => point.address_space, + } as u64; + (x, y) + }) + .collect(); - datapoints_for_ops.push( datapoints ); + datapoints_for_ops.push(datapoints); } - finalize_datapoints( xs.into_iter().collect(), datapoints_for_ops ) + finalize_datapoints(xs.into_iter().collect(), datapoints_for_ops) } impl Graph { @@ -1125,223 +1235,251 @@ impl Graph { gradient: None, kind: None, - cached_datapoints: None + cached_datapoints: None, } } - fn add_with_label( &mut self, label: String, list: AllocationList ) -> Result< Self, Box< rhai::EvalAltResult > > { + fn add_with_label( + &mut self, + label: String, + list: AllocationList, + ) -> Result> { self.bail_unless_allocation_graph()?; let mut cloned = self.clone(); - cloned.allocation_lists.push( list ); - cloned.labels.push( Some( label ) ); + cloned.allocation_lists.push(list); + cloned.labels.push(Some(label)); cloned.cached_datapoints = None; - Ok( cloned ) + Ok(cloned) } - fn add_group( &mut self, group: AllocationGroupList ) -> Result< Self, Box< rhai::EvalAltResult > > { + fn add_group(&mut self, group: AllocationGroupList) -> Result> { self.bail_unless_allocation_graph()?; let mut cloned = self.clone(); for list in group { - cloned.allocation_lists.push( list ); - cloned.labels.push( None ); + cloned.allocation_lists.push(list); + cloned.labels.push(None); } cloned.cached_datapoints = None; - Ok( cloned ) + Ok(cloned) } - fn add( &mut self, list: AllocationList ) -> Result< Self, Box< rhai::EvalAltResult > > { + fn add(&mut self, list: AllocationList) -> Result> { self.bail_unless_allocation_graph()?; let mut cloned = self.clone(); - cloned.allocation_lists.push( list ); - cloned.labels.push( None ); + cloned.allocation_lists.push(list); + cloned.labels.push(None); cloned.cached_datapoints = None; - Ok( cloned ) + Ok(cloned) } - fn add_maps_with_label( &mut self, label: String, list: MapList ) -> Result< Self, Box< rhai::EvalAltResult > > { + fn add_maps_with_label( + &mut self, + label: String, + list: MapList, + ) -> Result> { self.bail_unless_map_graph()?; let mut cloned = self.clone(); - cloned.map_lists.push( list ); - cloned.labels.push( Some( label ) ); + cloned.map_lists.push(list); + cloned.labels.push(Some(label)); cloned.cached_datapoints = None; - Ok( cloned ) + Ok(cloned) } - fn add_maps( &mut self, list: MapList ) -> Result< Self, Box< rhai::EvalAltResult > > { + fn add_maps(&mut self, list: MapList) -> Result> { self.bail_unless_map_graph()?; let mut cloned = self.clone(); - cloned.map_lists.push( list ); - cloned.labels.push( None ); + cloned.map_lists.push(list); + cloned.labels.push(None); cloned.cached_datapoints = None; - Ok( cloned ) + Ok(cloned) } - fn only_non_empty_series( &mut self ) -> Self { + fn only_non_empty_series(&mut self) -> Self { let mut cloned = self.clone(); cloned.hide_empty = true; cloned } - fn trim( &mut self ) -> Self { + fn trim(&mut self) -> Self { let mut cloned = self.clone(); cloned.trim_left = true; cloned.trim_right = true; cloned } - fn trim_left( &mut self ) -> Self { + fn trim_left(&mut self) -> Self { let mut cloned = self.clone(); cloned.trim_left = true; cloned } - fn trim_right( &mut self ) -> Self { + fn trim_right(&mut self) -> Self { let mut cloned = self.clone(); cloned.trim_right = true; cloned } - fn start_at( &mut self, offset: Duration ) -> Self { + fn start_at(&mut self, offset: Duration) -> Self { let mut cloned = self.clone(); - cloned.start_at = Some( offset ); + cloned.start_at = Some(offset); cloned } - fn end_at( &mut self, offset: Duration ) -> Self { + fn end_at(&mut self, offset: Duration) -> Self { let mut cloned = self.clone(); - cloned.end_at = Some( offset ); + cloned.end_at = Some(offset); cloned } - fn without_legend( &mut self ) -> Self { + fn without_legend(&mut self) -> Self { let mut cloned = self.clone(); cloned.without_legend = true; cloned } - fn without_axes( &mut self ) -> Self { + fn without_axes(&mut self) -> Self { let mut cloned = self.clone(); cloned.without_axes = true; cloned } - fn without_grid( &mut self ) -> Self { + fn without_grid(&mut self) -> Self { let mut cloned = self.clone(); cloned.without_grid = true; cloned } - fn bail_unless_allocation_graph( &self ) -> Result< (), Box< rhai::EvalAltResult > > { - if !self.graph_kind().map( |kind| kind.is_for_allocations() ).unwrap_or( true ) { - return Err( error( "not an allocation graph" ) ); + fn bail_unless_allocation_graph(&self) -> Result<(), Box> { + if !self + .graph_kind() + .map(|kind| kind.is_for_allocations()) + .unwrap_or(true) + { + return Err(error("not an allocation graph")); } Ok(()) } - fn bail_unless_map_graph( &self ) -> Result< (), Box< rhai::EvalAltResult > > { - if !self.graph_kind().map( |kind| kind.is_for_maps() ).unwrap_or( true ) { - return Err( error( "not a map graph" ) ); + fn bail_unless_map_graph(&self) -> Result<(), Box> { + if !self + .graph_kind() + .map(|kind| kind.is_for_maps()) + .unwrap_or(true) + { + return Err(error("not a map graph")); } Ok(()) } - fn show_memory_usage( &mut self ) -> Result< Self, Box< rhai::EvalAltResult > > { + fn show_memory_usage(&mut self) -> Result> { self.bail_unless_allocation_graph()?; let mut cloned = self.clone(); - cloned.kind = Some( GraphKind::Allocation( AllocationGraphKind::MemoryUsage ) ); + cloned.kind = Some(GraphKind::Allocation(AllocationGraphKind::MemoryUsage)); cloned.cached_datapoints = None; - Ok( cloned ) + Ok(cloned) } - fn show_live_allocations( &mut self ) -> Result< Self, Box< rhai::EvalAltResult > > { + fn show_live_allocations(&mut self) -> Result> { self.bail_unless_allocation_graph()?; let mut cloned = self.clone(); - cloned.kind = Some( GraphKind::Allocation( AllocationGraphKind::LiveAllocations ) ); + cloned.kind = Some(GraphKind::Allocation(AllocationGraphKind::LiveAllocations)); cloned.cached_datapoints = None; - Ok( cloned ) + Ok(cloned) } - fn show_new_allocations( &mut self ) -> Result< Self, Box< rhai::EvalAltResult > > { + fn show_new_allocations(&mut self) -> Result> { self.bail_unless_allocation_graph()?; let mut cloned = self.clone(); - cloned.kind = Some( GraphKind::Allocation( AllocationGraphKind::NewAllocations ) ); + cloned.kind = Some(GraphKind::Allocation(AllocationGraphKind::NewAllocations)); cloned.cached_datapoints = None; - Ok( cloned ) + Ok(cloned) } - fn show_deallocations( &mut self ) -> Result< Self, Box< rhai::EvalAltResult > > { + fn show_deallocations(&mut self) -> Result> { self.bail_unless_allocation_graph()?; let mut cloned = self.clone(); - cloned.kind = Some( GraphKind::Allocation( AllocationGraphKind::Deallocations ) ); + cloned.kind = Some(GraphKind::Allocation(AllocationGraphKind::Deallocations)); cloned.cached_datapoints = None; - Ok( cloned ) + Ok(cloned) } - fn show_rss( &mut self ) -> Result< Self, Box< rhai::EvalAltResult > > { + fn show_rss(&mut self) -> Result> { self.bail_unless_map_graph()?; let mut cloned = self.clone(); - cloned.kind = Some( GraphKind::Map( MapGraphKind::RSS ) ); + cloned.kind = Some(GraphKind::Map(MapGraphKind::RSS)); cloned.cached_datapoints = None; - Ok( cloned ) + Ok(cloned) } - fn show_address_space( &mut self ) -> Result< Self, Box< rhai::EvalAltResult > > { + fn show_address_space(&mut self) -> Result> { self.bail_unless_map_graph()?; let mut cloned = self.clone(); - cloned.kind = Some( GraphKind::Map( MapGraphKind::AddressSpace ) ); + cloned.kind = Some(GraphKind::Map(MapGraphKind::AddressSpace)); cloned.cached_datapoints = None; - Ok( cloned ) + Ok(cloned) } - fn generate_allocation_ops( &mut self ) -> Result< Vec< Vec< OperationId > >, Box< rhai::EvalAltResult > > { + fn generate_allocation_ops( + &mut self, + ) -> Result>, Box> { self.bail_unless_allocation_graph()?; let lists = &mut self.allocation_lists; if lists.is_empty() { - return Err( error( "no allocation lists given" ) ); + return Err(error("no allocation lists given")); } - let data = lists[ 0 ].data.clone(); - if !lists.iter().all( |list| list.data.id() == data.id() ) { - return Err( error( "not every allocation list given is from the same data file" ) ); + let data = lists[0].data.clone(); + if !lists.iter().all(|list| list.data.id() == data.id()) { + return Err(error( + "not every allocation list given is from the same data file", + )); } - let threshold = self.end_at.map( |offset| data.initial_timestamp + offset.0 ).unwrap_or( data.last_timestamp ); + let threshold = self + .end_at + .map(|offset| data.initial_timestamp + offset.0) + .unwrap_or(data.last_timestamp); let mut seen = HashSet::new(); - let ops_for_list: Vec< _ > = lists.iter_mut().map( |list| - list.filtered_ops( |id| { - if !seen.insert( id ) { - return OpFilter::None; - } + let ops_for_list: Vec<_> = lists + .iter_mut() + .map(|list| { + list.filtered_ops(|id| { + if !seen.insert(id) { + return OpFilter::None; + } - let allocation = data.get_allocation( id ); - if allocation.timestamp > threshold { - return OpFilter::None; - } + let allocation = data.get_allocation(id); + if allocation.timestamp > threshold { + return OpFilter::None; + } - if let Some( ref deallocation ) = allocation.deallocation { - if deallocation.timestamp > threshold { - return OpFilter::OnlyAlloc; + if let Some(ref deallocation) = allocation.deallocation { + if deallocation.timestamp > threshold { + return OpFilter::OnlyAlloc; + } } - } - OpFilter::Both + OpFilter::Both + }) }) - ).collect(); + .collect(); - Ok( ops_for_list ) + Ok(ops_for_list) } - fn generate_map_ops( &mut self ) -> Result< Vec< Vec< (Timestamp, UsageDelta) > >, Box< rhai::EvalAltResult > > { + fn generate_map_ops( + &mut self, + ) -> Result>, Box> { self.bail_unless_map_graph()?; let lists = &mut self.map_lists; if lists.is_empty() { - return Err( error( "no allocation lists given" ) ); + return Err(error("no allocation lists given")); } - let data = lists[ 0 ].data.clone(); - if !lists.iter().all( |list| list.data.id() == data.id() ) { - return Err( error( "not every map list given is from the same data file" ) ); + let data = lists[0].data.clone(); + if !lists.iter().all(|list| list.data.id() == data.id()) { + return Err(error("not every map list given is from the same data file")); } for list in lists.iter_mut() { @@ -1349,98 +1487,115 @@ impl Graph { } let mut seen = HashSet::new(); - let ops_for_list: Vec< _ > = lists.iter().map( |list| { - let ids = list.unfiltered_ids_iter(); - let mut ops = Vec::with_capacity( ids.len() ); - for map_id in ids { - if !seen.insert( map_id ) { - continue; + let ops_for_list: Vec<_> = lists + .iter() + .map(|list| { + let ids = list.unfiltered_ids_iter(); + let mut ops = Vec::with_capacity(ids.len()); + for map_id in ids { + if !seen.insert(map_id) { + continue; + } + data.get_map(map_id).emit_ops(&mut ops); } - data.get_map( map_id ).emit_ops( &mut ops ); - } - ops.par_sort_by_key( |(timestamp, _)| *timestamp ); - ops - }).collect(); + ops.par_sort_by_key(|(timestamp, _)| *timestamp); + ops + }) + .collect(); - Ok( ops_for_list ) + Ok(ops_for_list) } - fn with_gradient_color_scheme( &mut self, start: String, end: String ) -> Result< Self, Box< rhai::EvalAltResult > > { + fn with_gradient_color_scheme( + &mut self, + start: String, + end: String, + ) -> Result> { let mut cloned = self.clone(); - cloned.gradient = Some( Arc::new( + cloned.gradient = Some(Arc::new( colorgrad::CustomGradient::new() - .html_colors( &[start.as_str(), end.as_str()] ) - .build().map_err( |err| { - error( format!( "failed to create a gradient: {}", err ) ) - })? + .html_colors(&[start.as_str(), end.as_str()]) + .build() + .map_err(|err| error(format!("failed to create a gradient: {}", err)))?, )); - return Ok( cloned ); + return Ok(cloned); } - fn graph_kind( &self ) -> Option< GraphKind > { - self.kind.or_else( || { + fn graph_kind(&self) -> Option { + self.kind.or_else(|| { if !self.allocation_lists.is_empty() { - Some( GraphKind::Allocation( AllocationGraphKind::MemoryUsage ) ) + Some(GraphKind::Allocation(AllocationGraphKind::MemoryUsage)) } else if !self.map_lists.is_empty() { - Some( GraphKind::Map( MapGraphKind::RSS ) ) + Some(GraphKind::Map(MapGraphKind::RSS)) } else { None } }) } - fn data( &self ) -> Option< DataRef > { + fn data(&self) -> Option { match self.graph_kind()? { - GraphKind::Allocation( .. ) => Some( self.allocation_lists[ 0 ].data.clone() ), - GraphKind::Map( .. ) => Some( self.map_lists[ 0 ].data.clone() ) + GraphKind::Allocation(..) => Some(self.allocation_lists[0].data.clone()), + GraphKind::Map(..) => Some(self.map_lists[0].data.clone()), } } - fn save_to_string_impl( &self, xs: &[u64], datapoints_for_ops: &[Vec< (u64, u64) >], labels: &[Option< String >] ) -> Result< String, String > { - let data = self.data().ok_or_else( || "empty graph".to_owned() )?; + fn save_to_string_impl( + &self, + xs: &[u64], + datapoints_for_ops: &[Vec<(u64, u64)>], + labels: &[Option], + ) -> Result { + let data = self.data().ok_or_else(|| "empty graph".to_owned())?; - let mut x_min = - if self.trim_left { - xs.first().copied().unwrap_or( 0 ) - } else { - data.initial_timestamp.as_usecs() - }; + let mut x_min = if self.trim_left { + xs.first().copied().unwrap_or(0) + } else { + data.initial_timestamp.as_usecs() + }; - let mut x_max = - if self.trim_right { - xs.last().copied().unwrap_or( 0 ) - } else { - data.last_timestamp.as_usecs() - }; + let mut x_max = if self.trim_right { + xs.last().copied().unwrap_or(0) + } else { + data.last_timestamp.as_usecs() + }; - if let Some( start_at ) = self.start_at { - x_min = std::cmp::max( x_min, (data.initial_timestamp + start_at.0).as_usecs() ); + if let Some(start_at) = self.start_at { + x_min = std::cmp::max(x_min, (data.initial_timestamp + start_at.0).as_usecs()); } - if let Some( end_at ) = self.end_at { + if let Some(end_at) = self.end_at { x_max = (data.initial_timestamp + end_at.0).as_usecs(); } - x_max = std::cmp::max( x_min, x_max ); + x_max = std::cmp::max(x_min, x_max); - let datapoints_for_ops: Vec< _ > = - if x_min > xs.first().copied().unwrap_or( 0 ) || x_max < xs.last().copied().unwrap_or( 0 ) { - datapoints_for_ops.iter().map( |list| { + let datapoints_for_ops: Vec<_> = if x_min > xs.first().copied().unwrap_or(0) + || x_max < xs.last().copied().unwrap_or(0) + { + datapoints_for_ops + .iter() + .map(|list| { let list = list.as_slice(); - let list = &list[ list.iter().take_while( |&&(x, _)| x < x_min ).count().. ]; - let list = &list[ ..list.len() - list.iter().rev().take_while( |&&(x, _)| x > x_max ).count() ]; + let list = &list[list.iter().take_while(|&&(x, _)| x < x_min).count()..]; + let list = &list + [..list.len() - list.iter().rev().take_while(|&&(x, _)| x > x_max).count()]; list - }).collect() - } else { - datapoints_for_ops.iter().map( |list| list.as_slice() ).collect() - }; + }) + .collect() + } else { + datapoints_for_ops + .iter() + .map(|list| list.as_slice()) + .collect() + }; let mut max_usage = 0; for &datapoints in &datapoints_for_ops { for (_, value) in datapoints { - max_usage = std::cmp::max( max_usage, *value ); + max_usage = std::cmp::max(max_usage, *value); } } @@ -1457,7 +1612,7 @@ impl Graph { impl Ranged for $kind { type FormatOption = plotters::coord::ranged1d::NoDefaultFormatting; type ValueType = u64; - fn map( &self, value: &Self::ValueType, limit: (i32, i32) ) -> i32 { + fn map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32 { if self.0 == self.1 { return (limit.1 - limit.0) / 2; } @@ -1471,32 +1626,37 @@ impl Graph { let data_offset = value - self.0; let data_relative_position = data_offset as f64 / data_range as f64; - limit.0 + (screen_range as f64 * data_relative_position + 1e-3).floor() as i32 + limit.0 + + (screen_range as f64 * data_relative_position + 1e-3).floor() as i32 } - fn key_points< Hint: plotters::coord::ranged1d::KeyPointHint >( &self, hint: Hint ) -> Vec< Self::ValueType > { - gen_keypoints( (self.0, self.1), hint.max_num_points() ) + fn key_points( + &self, + hint: Hint, + ) -> Vec { + gen_keypoints((self.0, self.1), hint.max_num_points()) } - fn range( &self ) -> std::ops::Range< Self::ValueType > { + fn range(&self) -> std::ops::Range { self.0..self.1 } } - } + }; } - struct SizeRange( u64, u64 ); + struct SizeRange(u64, u64); - impl plotters::coord::ranged1d::ValueFormatter< u64 > for SizeRange { - fn format( value: &u64 ) -> String { - SCALE_Y.with( |cell| { + impl plotters::coord::ranged1d::ValueFormatter for SizeRange { + fn format(value: &u64) -> String { + SCALE_Y.with(|cell| { let (min, max) = cell.get(); if max < 1024 { - format!( "{}", value ) + format!("{}", value) } else { - match KIND.with( |cell| cell.get() ) { - GraphKind::Allocation( AllocationGraphKind::MemoryUsage ) | GraphKind::Map( _ ) => { + match KIND.with(|cell| cell.get()) { + GraphKind::Allocation(AllocationGraphKind::MemoryUsage) + | GraphKind::Map(_) => { let (unit, multiplier) = { if max < 1024 * 1024 { ("KB", 1024) @@ -1506,14 +1666,18 @@ impl Graph { }; if max - min <= (10 * multiplier) { - format!( "{:.02} {}", *value as f64 / multiplier as f64, unit ) + format!("{:.02} {}", *value as f64 / multiplier as f64, unit) } else if max - min <= (100 * multiplier) { - format!( "{:.01} {}", *value as f64 / multiplier as f64, unit ) + format!("{:.01} {}", *value as f64 / multiplier as f64, unit) } else { - format!( "{} {}", value / multiplier, unit ) + format!("{} {}", value / multiplier, unit) } - }, - GraphKind::Allocation( AllocationGraphKind::LiveAllocations | AllocationGraphKind::NewAllocations | AllocationGraphKind::Deallocations ) => { + } + GraphKind::Allocation( + AllocationGraphKind::LiveAllocations + | AllocationGraphKind::NewAllocations + | AllocationGraphKind::Deallocations, + ) => { let (unit, multiplier) = { if max < 1000 * 1000 { ("K", 1000) @@ -1523,11 +1687,11 @@ impl Graph { }; if max - min <= (10 * multiplier) { - format!( "{:.02} {}", *value as f64 / multiplier as f64, unit ) + format!("{:.02} {}", *value as f64 / multiplier as f64, unit) } else if max - min <= (100 * multiplier) { - format!( "{:.01} {}", *value as f64 / multiplier as f64, unit ) + format!("{:.01} {}", *value as f64 / multiplier as f64, unit) } else { - format!( "{} {}", value / multiplier, unit ) + format!("{} {}", value / multiplier, unit) } } } @@ -1536,136 +1700,182 @@ impl Graph { } } - impl_ranged!( SizeRange ); + impl_ranged!(SizeRange); - struct TimeRange( u64, u64 ); + struct TimeRange(u64, u64); - impl plotters::coord::ranged1d::ValueFormatter< u64 > for TimeRange { - fn format( value: &u64 ) -> String { + impl plotters::coord::ranged1d::ValueFormatter for TimeRange { + fn format(value: &u64) -> String { use chrono::prelude::*; - SCALE_X.with( |cell| { + SCALE_X.with(|cell| { let (min, max) = cell.get(); - debug_assert!( *value >= min ); - - let start = to_chrono( min ); - let end = to_chrono( max ); - let ts = to_chrono( *value ); - if start.year() == end.year() && start.month() == end.month() && start.day() == end.day() { - format!( "{:02}:{:02}:{:02}", ts.hour(), ts.minute(), ts.second() ) + debug_assert!(*value >= min); + + let start = to_chrono(min); + let end = to_chrono(max); + let ts = to_chrono(*value); + if start.year() == end.year() + && start.month() == end.month() + && start.day() == end.day() + { + format!("{:02}:{:02}:{:02}", ts.hour(), ts.minute(), ts.second()) } else if start.year() == end.year() && start.month() == end.month() { - format!( "{:02} {:02}:{:02}:{:02}", ts.day(), ts.hour(), ts.minute(), ts.second() ) + format!( + "{:02} {:02}:{:02}:{:02}", + ts.day(), + ts.hour(), + ts.minute(), + ts.second() + ) } else if start.year() == end.year() { - format!( "{:02}-{:02} {:02}:{:02}:{:02}", ts.month(), ts.day(), ts.hour(), ts.minute(), ts.second() ) + format!( + "{:02}-{:02} {:02}:{:02}:{:02}", + ts.month(), + ts.day(), + ts.hour(), + ts.minute(), + ts.second() + ) } else { - format!( "{}-{:02}-{:02} {:02}:{:02}:{:02}", ts.year(), ts.month(), ts.day(), ts.hour(), ts.minute(), ts.second() ) + format!( + "{}-{:02}-{:02} {:02}:{:02}:{:02}", + ts.year(), + ts.month(), + ts.day(), + ts.hour(), + ts.minute(), + ts.second() + ) } }) } } - impl_ranged!( TimeRange ); + impl_ranged!(TimeRange); - struct TimeRangeOffset( u64, u64 ); + struct TimeRangeOffset(u64, u64); - impl plotters::coord::ranged1d::ValueFormatter< u64 > for TimeRangeOffset { - fn format( value: &u64 ) -> String { - SCALE_X_TOTAL.with( |cell| { + impl plotters::coord::ranged1d::ValueFormatter for TimeRangeOffset { + fn format(value: &u64) -> String { + SCALE_X_TOTAL.with(|cell| { let (min, _max) = cell.get(); - debug_assert!( *value >= min ); + debug_assert!(*value >= min); let relative = *value - min; let relative_s = relative / 1_000_000; if relative == 0 { - format!( "0" ) + format!("0") } else if relative < 1_000 { - format!( "+{}us", relative ) + format!("+{}us", relative) } else if relative < 1_000_000 { - format!( "+{}ms", relative / 1_000 ) + format!("+{}ms", relative / 1_000) } else if relative < 60_000_000 { - format!( "+{}s", relative / 1_000_000 ) + format!("+{}s", relative / 1_000_000) } else { let rh = relative_s / 3600; let rm = (relative_s - rh * 3600) / 60; let rs = relative_s - rh * 3600 - rm * 60; - return format!( "+{:02}:{:02}:{:02}", rh, rm, rs ); + return format!("+{:02}:{:02}:{:02}", rh, rm, rs); } }) } } - impl_ranged!( TimeRangeOffset ); + impl_ranged!(TimeRangeOffset); - let graph_kind = self.graph_kind().unwrap_or( GraphKind::Allocation( AllocationGraphKind::MemoryUsage ) ); + let graph_kind = self + .graph_kind() + .unwrap_or(GraphKind::Allocation(AllocationGraphKind::MemoryUsage)); - SCALE_X.with( |cell| cell.set( (x_min, x_max + 1) ) ); - SCALE_X_TOTAL.with( |cell| cell.set( (data.initial_timestamp.as_usecs(), data.last_timestamp.as_usecs() + 1) ) ); - SCALE_Y.with( |cell| cell.set( (0, (max_usage + 1) as u64) ) ); - KIND.with( |cell| cell.set( graph_kind ) ); + SCALE_X.with(|cell| cell.set((x_min, x_max + 1))); + SCALE_X_TOTAL.with(|cell| { + cell.set(( + data.initial_timestamp.as_usecs(), + data.last_timestamp.as_usecs() + 1, + )) + }); + SCALE_Y.with(|cell| cell.set((0, (max_usage + 1) as u64))); + KIND.with(|cell| cell.set(graph_kind)); let mut output = String::new(); use plotters::prelude::*; - let root = SVGBackend::with_string( &mut output, (1024, 768) ).into_drawing_area(); - root.fill( &WHITE ).map_err( |error| format!( "failed to fill the graph with white: {}", error ) )?; + let root = SVGBackend::with_string(&mut output, (1024, 768)).into_drawing_area(); + root.fill(&WHITE) + .map_err(|error| format!("failed to fill the graph with white: {}", error))?; - let mut chart = ChartBuilder::on( &root ); + let mut chart = ChartBuilder::on(&root); let mut chart = &mut chart; if !self.without_axes { chart = chart - .margin( (1).percent() ) - .set_label_area_size( LabelAreaPosition::Left, 70 ) - .margin_right( 50 ) - .set_label_area_size( LabelAreaPosition::Bottom, 60 ) - .set_label_area_size( LabelAreaPosition::Top, 60 ) + .margin((1).percent()) + .set_label_area_size(LabelAreaPosition::Left, 70) + .margin_right(50) + .set_label_area_size(LabelAreaPosition::Bottom, 60) + .set_label_area_size(LabelAreaPosition::Top, 60) }; - let mut chart = chart.build_cartesian_2d( - TimeRange( x_min, x_max + 1 ), - SizeRange( 0, (max_usage + 1) as u64 ) + let mut chart = chart + .build_cartesian_2d( + TimeRange(x_min, x_max + 1), + SizeRange(0, (max_usage + 1) as u64), ) - .map_err( |error| format!( "failed to construct the chart builder: {}", error ) )? + .map_err(|error| format!("failed to construct the chart builder: {}", error))? .set_secondary_coord( - TimeRangeOffset( x_min, x_max + 1 ), - SizeRange( 0, (max_usage + 1) as u64 ) + TimeRangeOffset(x_min, x_max + 1), + SizeRange(0, (max_usage + 1) as u64), ); let mut colors = Vec::new(); - if let Some( ref gradient ) = self.gradient { - let step = 1.0 / (std::cmp::max( 1, datapoints_for_ops.len() ) - 1) as f64; + if let Some(ref gradient) = self.gradient { + let step = 1.0 / (std::cmp::max(1, datapoints_for_ops.len()) - 1) as f64; for index in 0..datapoints_for_ops.len() { let position = index as f64 * step; - let color = gradient.at( position ); - let color_rgb = color.rgba_u8(); - colors.push( RGBColor( color_rgb.0, color_rgb.1, color_rgb.2 ).to_rgba().mix( color.alpha() ) ); + let color = gradient.at(position); + let color_rgb = color.to_rgba8(); + colors.push( + RGBColor(color_rgb[0], color_rgb[1], color_rgb[2]) + .to_rgba() + .mix(color.to_linear_rgba().3), + ); } } else { for index in 0..datapoints_for_ops.len() { - colors.push( Palette99::pick( index ).to_rgba() ); + colors.push(Palette99::pick(index).to_rgba()); } } - for ((datapoints, label), color) in datapoints_for_ops.iter().zip( labels.iter() ).rev().zip( colors.into_iter().rev() ) { - let series = chart.draw_series( - AreaSeries::new( - datapoints.iter().map( |&(x, y)| { - (x, y as u64) - }).chain( std::iter::once(( - x_max, - datapoints.last().copied().map( |(_, y)| y ).unwrap_or( 0 ) - ))), - 0_u64, - color, - ).border_style( color.stroke_width( 1 ) ), - ).map_err( |error| format!( "failed to draw a series: {}", error ) )?; - - if let Some( label ) = label { + for ((datapoints, label), color) in datapoints_for_ops + .iter() + .zip(labels.iter()) + .rev() + .zip(colors.into_iter().rev()) + { + let series = chart + .draw_series( + AreaSeries::new( + datapoints + .iter() + .map(|&(x, y)| (x, y as u64)) + .chain(std::iter::once(( + x_max, + datapoints.last().copied().map(|(_, y)| y).unwrap_or(0), + ))), + 0_u64, + color, + ) + .border_style(color.stroke_width(1)), + ) + .map_err(|error| format!("failed to draw a series: {}", error))?; + + if let Some(label) = label { if datapoints.is_empty() && self.hide_empty || self.without_legend { continue; } - series - .label( label ) - .legend( move |(x, y)| Rectangle::new( [(x, y - 5), (x + 10, y + 5)], color.filled() ) ); + series.label(label).legend(move |(x, y)| { + Rectangle::new([(x, y - 5), (x + 10, y + 5)], color.filled()) + }); } } @@ -1673,203 +1883,252 @@ impl Graph { let mut mesh = &mut mesh; if !self.without_axes { let label = match graph_kind { - GraphKind::Allocation( AllocationGraphKind::MemoryUsage ) => "Memory usage", - GraphKind::Allocation( AllocationGraphKind::LiveAllocations ) => "Live allocations", - GraphKind::Allocation( AllocationGraphKind::NewAllocations ) => "New allocations", - GraphKind::Allocation( AllocationGraphKind::Deallocations ) => "Deallocations", - GraphKind::Map( MapGraphKind::RSS ) => "RSS", - GraphKind::Map( MapGraphKind::AddressSpace ) => "Address space", + GraphKind::Allocation(AllocationGraphKind::MemoryUsage) => "Memory usage", + GraphKind::Allocation(AllocationGraphKind::LiveAllocations) => "Live allocations", + GraphKind::Allocation(AllocationGraphKind::NewAllocations) => "New allocations", + GraphKind::Allocation(AllocationGraphKind::Deallocations) => "Deallocations", + GraphKind::Map(MapGraphKind::RSS) => "RSS", + GraphKind::Map(MapGraphKind::AddressSpace) => "Address space", }; - mesh = mesh.x_desc( "Time" ).y_desc( label ); + mesh = mesh.x_desc("Time").y_desc(label); } if self.without_grid { mesh = mesh.disable_mesh(); } - mesh.draw().map_err( |error| format!( "failed to draw the mesh: {}", error ) )?; + mesh.draw() + .map_err(|error| format!("failed to draw the mesh: {}", error))?; if !self.without_axes { chart .configure_secondary_axes() .draw() - .map_err( |error| format!( "failed to draw the secondary axes: {}", error ) )?; + .map_err(|error| format!("failed to draw the secondary axes: {}", error))?; } - if labels.iter().any( |label| label.is_some() ) && !self.without_legend { + if labels.iter().any(|label| label.is_some()) && !self.without_legend { chart .configure_series_labels() - .background_style( &WHITE.mix( 0.75 ) ) - .border_style( &BLACK ) - .position( SeriesLabelPosition::UpperLeft ) + .background_style(&WHITE.mix(0.75)) + .border_style(&BLACK) + .position(SeriesLabelPosition::UpperLeft) .draw() - .map_err( |error| format!( "failed to draw the legend: {}", error ) )?; + .map_err(|error| format!("failed to draw the legend: {}", error))?; } - root.present().map_err( |error| format!( "failed to present the graph: {}", error ) )?; - std::mem::drop( chart ); - std::mem::drop( root ); + root.present() + .map_err(|error| format!("failed to present the graph: {}", error))?; + std::mem::drop(chart); + std::mem::drop(root); - Ok( output ) + Ok(output) } - fn save_to_string( &mut self ) -> Result< String, Box< rhai::EvalAltResult > > { + fn save_to_string(&mut self) -> Result> { (|| { - if self.cached_datapoints.is_none() { - let (xs, datapoints_for_ops) = match self.graph_kind() { - Some( GraphKind::Allocation( kind ) ) => { - let ops_for_list = self.generate_allocation_ops()?; - prepare_allocation_graph_datapoints( &self.allocation_lists[ 0 ].data, &ops_for_list, kind ) - }, - Some( GraphKind::Map( kind ) ) => { - let ops_for_list = self.generate_map_ops()?; - prepare_map_graph_datapoints( &ops_for_list, kind ) - }, - None => Default::default() - }; + { + if self.cached_datapoints.is_none() { + let (xs, datapoints_for_ops) = match self.graph_kind() { + Some(GraphKind::Allocation(kind)) => { + let ops_for_list = self.generate_allocation_ops()?; + prepare_allocation_graph_datapoints( + &self.allocation_lists[0].data, + &ops_for_list, + kind, + ) + } + Some(GraphKind::Map(kind)) => { + let ops_for_list = self.generate_map_ops()?; + prepare_map_graph_datapoints(&ops_for_list, kind) + } + None => Default::default(), + }; - self.cached_datapoints = Some( Arc::new( (xs, datapoints_for_ops) ) ); - } + self.cached_datapoints = Some(Arc::new((xs, datapoints_for_ops))); + } - let cached = self.cached_datapoints.as_ref().unwrap(); - self.save_to_string_impl( &cached.0, &cached.1, &self.labels ) - }.map_err( |error| { - Box::new( rhai::EvalAltResult::from( format!( "failed to generate a graph: {}", error ) ) ) - }))() + let cached = self.cached_datapoints.as_ref().unwrap(); + self.save_to_string_impl(&cached.0, &cached.1, &self.labels) + } + .map_err(|error| { + Box::new(rhai::EvalAltResult::from(format!( + "failed to generate a graph: {}", + error + ))) + }) + })() } - fn save( &mut self, env: &mut dyn Environment, path: String ) -> Result< Self, Box< rhai::EvalAltResult > > { + fn save( + &mut self, + env: &mut dyn Environment, + path: String, + ) -> Result> { let data = self.save_to_string()?; - env.file_write( &path, FileKind::Svg, data.as_bytes() )?; - Ok( self.clone() ) + env.file_write(&path, FileKind::Svg, data.as_bytes())?; + Ok(self.clone()) } - fn save_each_series_as_graph( &mut self, env: &mut dyn Environment, mut path: String ) -> Result< Self, Box< rhai::EvalAltResult > > { - env.mkdir_p( &path )?; + fn save_each_series_as_graph( + &mut self, + env: &mut dyn Environment, + mut path: String, + ) -> Result> { + env.mkdir_p(&path)?; if path == "." { path = "".into(); - } else if !path.ends_with( '/' ) { - path.push( '/' ); + } else if !path.ends_with('/') { + path.push('/'); } match self.graph_kind() { - Some( GraphKind::Allocation( kind ) ) => { + Some(GraphKind::Allocation(kind)) => { let ops_for_list = self.generate_allocation_ops()?; - for (index, (ops, label)) in ops_for_list.into_iter().zip( self.labels.iter() ).enumerate() { - let (xs, datapoints_for_ops) = prepare_allocation_graph_datapoints( &self.allocation_lists[ 0 ].data, &[ops], kind ); - let data = self.save_to_string_impl( &xs, &datapoints_for_ops, &[label.clone()] )?; - - let file_path = - if let Some( label ) = label { - format!( "{}{}.svg", path, label ) - } else { - format!( "{}Series #{}.svg", path, index ) - }; + for (index, (ops, label)) in + ops_for_list.into_iter().zip(self.labels.iter()).enumerate() + { + let (xs, datapoints_for_ops) = prepare_allocation_graph_datapoints( + &self.allocation_lists[0].data, + &[ops], + kind, + ); + let data = + self.save_to_string_impl(&xs, &datapoints_for_ops, &[label.clone()])?; + + let file_path = if let Some(label) = label { + format!("{}{}.svg", path, label) + } else { + format!("{}Series #{}.svg", path, index) + }; - env.file_write( &file_path, FileKind::Svg, data.as_bytes() )?; + env.file_write(&file_path, FileKind::Svg, data.as_bytes())?; } - }, - Some( GraphKind::Map( kind ) ) => { + } + Some(GraphKind::Map(kind)) => { let ops_for_list = self.generate_map_ops()?; - for (index, (ops, label)) in ops_for_list.into_iter().zip( self.labels.iter() ).enumerate() { - let (xs, datapoints_for_ops) = prepare_map_graph_datapoints( &[ops], kind ); - let data = self.save_to_string_impl( &xs, &datapoints_for_ops, &[label.clone()] )?; - - let file_path = - if let Some( label ) = label { - format!( "{}{}.svg", path, label ) - } else { - format!( "{}Series #{}.svg", path, index ) - }; + for (index, (ops, label)) in + ops_for_list.into_iter().zip(self.labels.iter()).enumerate() + { + let (xs, datapoints_for_ops) = prepare_map_graph_datapoints(&[ops], kind); + let data = + self.save_to_string_impl(&xs, &datapoints_for_ops, &[label.clone()])?; + + let file_path = if let Some(label) = label { + format!("{}{}.svg", path, label) + } else { + format!("{}Series #{}.svg", path, index) + }; - env.file_write( &file_path, FileKind::Svg, data.as_bytes() )?; + env.file_write(&file_path, FileKind::Svg, data.as_bytes())?; } - }, + } None => {} }; - Ok( self.clone() ) + Ok(self.clone()) } - fn save_each_series_as_flamegraph( &mut self, env: &mut dyn Environment, mut path: String ) -> Result< Self, Box< rhai::EvalAltResult > > { - if !self.graph_kind().map( |kind| kind.is_for_allocations() ).unwrap_or( true ) { - return Err( error( "only allocation graphs can be saved as a flamegraph" ) ); + fn save_each_series_as_flamegraph( + &mut self, + env: &mut dyn Environment, + mut path: String, + ) -> Result> { + if !self + .graph_kind() + .map(|kind| kind.is_for_allocations()) + .unwrap_or(true) + { + return Err(error("only allocation graphs can be saved as a flamegraph")); } - env.mkdir_p( &path )?; + env.mkdir_p(&path)?; if path == "." { path = "".into(); - } else if !path.ends_with( '/' ) { - path.push( '/' ); + } else if !path.ends_with('/') { + path.push('/'); } let ops_for_list = self.generate_allocation_ops()?; - for (index, ((list, ops), label)) in self.allocation_lists.iter().zip( ops_for_list ).zip( self.labels.iter() ).enumerate() { - let ids: HashSet< _ > = ops.into_iter().map( |op| op.id() ).collect(); + for (index, ((list, ops), label)) in self + .allocation_lists + .iter() + .zip(ops_for_list) + .zip(self.labels.iter()) + .enumerate() + { + let ids: HashSet<_> = ops.into_iter().map(|op| op.id()).collect(); let mut list = AllocationList { data: list.data.clone(), - allocation_ids: Some( Arc::new( ids.into_iter().collect() ) ), - filter: None + allocation_ids: Some(Arc::new(ids.into_iter().collect())), + filter: None, }; - let file_path = - if let Some( label ) = label { - format!( "{}{}.svg", path, label ) - } else { - format!( "{}Series #{}.svg", path, index ) - }; + let file_path = if let Some(label) = label { + format!("{}{}.svg", path, label) + } else { + format!("{}Series #{}.svg", path, index) + }; - list.save_as_flamegraph( env, file_path )?; + list.save_as_flamegraph(env, file_path)?; } - Ok( self.clone() ) + Ok(self.clone()) } } -fn load( path: String ) -> Result< Arc< Data >, Box< rhai::EvalAltResult > > { - info!( "Loading {:?}...", path ); - let fp = File::open( &path ) - .map_err( |error| rhai::EvalAltResult::from( format!( "failed to open '{}': {}", path, error ) ) ) - .map_err( Box::new )?; +fn load(path: String) -> Result, Box> { + info!("Loading {:?}...", path); + let fp = File::open(&path) + .map_err(|error| rhai::EvalAltResult::from(format!("failed to open '{}': {}", path, error))) + .map_err(Box::new)?; let debug_symbols: &[PathBuf] = &[]; - let data = Loader::load_from_stream( fp, debug_symbols ) - .map_err( |error| rhai::EvalAltResult::from( format!( "failed to load '{}': {}", path, error ) ) ) - .map_err( Box::new )?; + let data = Loader::load_from_stream(fp, debug_symbols) + .map_err(|error| rhai::EvalAltResult::from(format!("failed to load '{}': {}", path, error))) + .map_err(Box::new)?; - Ok( Arc::new( data ) ) + Ok(Arc::new(data)) } -pub fn error( message: impl Into< String > ) -> Box< rhai::EvalAltResult > { - Box::new( rhai::EvalAltResult::from( message.into() ) ) +pub fn error(message: impl Into) -> Box { + Box::new(rhai::EvalAltResult::from(message.into())) } #[derive(Copy, Clone)] pub enum FileKind { - Svg + Svg, } pub struct Engine { - inner: rhai::Engine + inner: rhai::Engine, } #[derive(Default)] pub struct EngineArgs { - pub argv: Vec< String >, - pub data: Option< Arc< Data > >, - pub allocation_ids: Option< Arc< Vec< AllocationId > > >, - pub map_ids: Option< Arc< Vec< MapId > > > + pub argv: Vec, + pub data: Option>, + pub allocation_ids: Option>>, + pub map_ids: Option>>, } pub trait Environment { - fn println( &mut self, message: &str ); - fn mkdir_p( &mut self, path: &str ) -> Result< (), Box< rhai::EvalAltResult > >; - fn chdir( &mut self, path: &str ) -> Result< (), Box< rhai::EvalAltResult > >; - fn file_write( &mut self, path: &str, kind: FileKind, contents: &[u8] ) -> Result< (), Box< rhai::EvalAltResult > >; - fn exit( &mut self, errorcode: Option< i32 > ) -> Result< (), Box< rhai::EvalAltResult > > { - Err( Box::new( rhai::EvalAltResult::Return( (errorcode.unwrap_or( 0 ) as i64).into(), rhai::Position::NONE ) ) ) - } - fn load( &mut self, _path: String ) -> Result< Arc< Data >, Box< rhai::EvalAltResult > > { - Err( error( "unsupported in this environment" ) ) + fn println(&mut self, message: &str); + fn mkdir_p(&mut self, path: &str) -> Result<(), Box>; + fn chdir(&mut self, path: &str) -> Result<(), Box>; + fn file_write( + &mut self, + path: &str, + kind: FileKind, + contents: &[u8], + ) -> Result<(), Box>; + fn exit(&mut self, errorcode: Option) -> Result<(), Box> { + Err(Box::new(rhai::EvalAltResult::Return( + (errorcode.unwrap_or(0) as i64).into(), + rhai::Position::NONE, + ))) + } + fn load(&mut self, _path: String) -> Result, Box> { + Err(error("unsupported in this environment")) } } @@ -1877,68 +2136,85 @@ pub trait Environment { pub struct NativeEnvironment {} impl Environment for NativeEnvironment { - fn println( &mut self, message: &str ) { - println!( "{}", message ); + fn println(&mut self, message: &str) { + println!("{}", message); } - fn mkdir_p( &mut self, path: &str ) -> Result< (), Box< rhai::EvalAltResult > > { - std::fs::create_dir_all( path ).map_err( |error| format!( "failed to create '{}': {}", path, error ).into() ).map_err( Box::new ) + fn mkdir_p(&mut self, path: &str) -> Result<(), Box> { + std::fs::create_dir_all(path) + .map_err(|error| format!("failed to create '{}': {}", path, error).into()) + .map_err(Box::new) } - fn chdir( &mut self, path: &str ) -> Result< (), Box< rhai::EvalAltResult > > { - std::env::set_current_dir( path ).map_err( |error| format!( "failed to chdir to '{}': {}", path, error ).into() ).map_err( Box::new ) + fn chdir(&mut self, path: &str) -> Result<(), Box> { + std::env::set_current_dir(path) + .map_err(|error| format!("failed to chdir to '{}': {}", path, error).into()) + .map_err(Box::new) } - fn file_write( &mut self, path: &str, _kind: FileKind, contents: &[u8] ) -> Result< (), Box< rhai::EvalAltResult > > { + fn file_write( + &mut self, + path: &str, + _kind: FileKind, + contents: &[u8], + ) -> Result<(), Box> { use std::io::Write; - let mut fp = File::create( &path ) - .map_err( |error| Box::new( rhai::EvalAltResult::from( format!( "failed to create {:?}: {}", path, error ) ) ) )?; + let mut fp = File::create(&path).map_err(|error| { + Box::new(rhai::EvalAltResult::from(format!( + "failed to create {:?}: {}", + path, error + ))) + })?; - fp.write_all( contents ) - .map_err( |error| Box::new( rhai::EvalAltResult::from( format!( "failed to write to {:?}: {}", path, error ) ) ) )?; + fp.write_all(contents).map_err(|error| { + Box::new(rhai::EvalAltResult::from(format!( + "failed to write to {:?}: {}", + path, error + ))) + })?; Ok(()) } - fn exit( &mut self, errorcode: Option< i32 > ) -> Result< (), Box< rhai::EvalAltResult > > { - std::process::exit( errorcode.unwrap_or( 0 ) ); + fn exit(&mut self, errorcode: Option) -> Result<(), Box> { + std::process::exit(errorcode.unwrap_or(0)); } - fn load( &mut self, path: String ) -> Result< Arc< Data >, Box< rhai::EvalAltResult > > { - load( path ) + fn load(&mut self, path: String) -> Result, Box> { + load(path) } } -fn to_string( value: rhai::plugin::Dynamic ) -> String { - if value.is::< String >() { - value.cast::< String >() - } else if value.is::< i64 >() { - value.cast::< i64 >().to_string() - } else if value.is::< u64 >() { - value.cast::< u64 >().to_string() - } else if value.is::< bool >() { - value.cast::< bool >().to_string() - } else if value.is::< f64 >() { - value.cast::< f64 >().to_string() - } else if value.is::< Duration >() { - value.cast::< Duration >().decompose().to_string() - } else if value.is::< Option< Duration > >() { - if let Some( duration ) = value.cast::< Option< Duration > >() { - format!( "Some({})", duration.decompose().to_string() ) +fn to_string(value: rhai::plugin::Dynamic) -> String { + if value.is::() { + value.cast::() + } else if value.is::() { + value.cast::().to_string() + } else if value.is::() { + value.cast::().to_string() + } else if value.is::() { + value.cast::().to_string() + } else if value.is::() { + value.cast::().to_string() + } else if value.is::() { + value.cast::().decompose().to_string() + } else if value.is::>() { + if let Some(duration) = value.cast::>() { + format!("Some({})", duration.decompose().to_string()) } else { "None".into() } - } else if value.is::< AllocationList >() { - let mut value = value.cast::< AllocationList >(); - format!( "{} allocation(s)", value.len() ) - } else if value.is::< MapList >() { - let mut value = value.cast::< MapList >(); - format!( "{} map(s)", value.len() ) - } else if value.is::< Backtrace >() { - value.cast::< Backtrace >().to_string() - } else if value.is::< Option< Backtrace > >() { - if let Some( backtrace ) = value.cast::< Option< Backtrace > >() { + } else if value.is::() { + let mut value = value.cast::(); + format!("{} allocation(s)", value.len()) + } else if value.is::() { + let mut value = value.cast::(); + format!("{} map(s)", value.len()) + } else if value.is::() { + value.cast::().to_string() + } else if value.is::>() { + if let Some(backtrace) = value.cast::>() { backtrace.to_string() } else { "None".into() @@ -1948,8 +2224,8 @@ fn to_string( value: rhai::plugin::Dynamic ) -> String { } } -fn format( fmt: &str, args: &[&str] ) -> Result< String, Box< rhai::EvalAltResult > > { - let mut output = String::with_capacity( fmt.len() ); +fn format(fmt: &str, args: &[&str]) -> Result> { + let mut output = String::with_capacity(fmt.len()); let mut tmp = String::new(); let mut in_interpolation = false; let mut current_arg = 0; @@ -1957,231 +2233,268 @@ fn format( fmt: &str, args: &[&str] ) -> Result< String, Box< rhai::EvalAltResul if in_interpolation { if tmp.is_empty() && ch == '{' { in_interpolation = false; - output.push( ch ); + output.push(ch); continue; } if ch == '}' { in_interpolation = false; if tmp.is_empty() { if current_arg >= args.len() { - return Err( error( "too many positional arguments in the format string" ) ); + return Err(error("too many positional arguments in the format string")); } - output.push_str( args[ current_arg ] ); + output.push_str(args[current_arg]); current_arg += 1; } else { - let position: Result< usize, _ > = tmp.parse(); - if let Ok( position ) = position { + let position: Result = tmp.parse(); + if let Ok(position) = position { tmp.clear(); if position >= args.len() { - return Err( error( format!( "invalid reference to positional argument {}", position ) ) ); + return Err(error(format!( + "invalid reference to positional argument {}", + position + ))); } - output.push_str( args[ position ] ); + output.push_str(args[position]); } else { - return Err( error( format!( "malformed positional argument \"{}\"", tmp ) ) ); + return Err(error(format!("malformed positional argument \"{}\"", tmp))); } } continue; } - tmp.push( ch ); + tmp.push(ch); } else { if ch == '{' { in_interpolation = true; continue; } - output.push( ch ); + output.push(ch); } } if in_interpolation { - return Err( error( "malformed format string" ) ); + return Err(error("malformed format string")); } - Ok( output ) + Ok(output) } impl Engine { - pub fn new( env: Arc< Mutex< dyn Environment > >, args: EngineArgs ) -> Self { + pub fn new(env: Arc>, args: EngineArgs) -> Self { use rhai::packages::Package; let mut engine = rhai::Engine::new_raw(); - engine.register_global_module( rhai::packages::ArithmeticPackage::new().as_shared_module() ); - engine.register_global_module( rhai::packages::BasicArrayPackage::new().as_shared_module() ); - engine.register_global_module( rhai::packages::BasicFnPackage::new().as_shared_module() ); - engine.register_global_module( rhai::packages::BasicIteratorPackage::new().as_shared_module() ); - engine.register_global_module( rhai::packages::BasicMapPackage::new().as_shared_module() ); - engine.register_global_module( rhai::packages::BasicMathPackage::new().as_shared_module() ); - engine.register_global_module( rhai::packages::BasicStringPackage::new().as_shared_module() ); - engine.register_global_module( rhai::packages::LogicPackage::new().as_shared_module() ); - engine.register_global_module( rhai::packages::MoreStringPackage::new().as_shared_module() ); + engine.register_global_module(rhai::packages::ArithmeticPackage::new().as_shared_module()); + engine.register_global_module(rhai::packages::BasicArrayPackage::new().as_shared_module()); + engine.register_global_module(rhai::packages::BasicFnPackage::new().as_shared_module()); + engine + .register_global_module(rhai::packages::BasicIteratorPackage::new().as_shared_module()); + engine.register_global_module(rhai::packages::BasicMapPackage::new().as_shared_module()); + engine.register_global_module(rhai::packages::BasicMathPackage::new().as_shared_module()); + engine.register_global_module(rhai::packages::BasicStringPackage::new().as_shared_module()); + engine.register_global_module(rhai::packages::LogicPackage::new().as_shared_module()); + engine.register_global_module(rhai::packages::MoreStringPackage::new().as_shared_module()); let argv = args.argv; // Utility functions. - engine.register_fn( "dirname", dirname ); - engine.register_fn( "h", |value: i64| Duration::from_secs( value as u64 * 3600 ) ); - engine.register_fn( "h", |value: f64| Duration::from_usecs( (value * 3600.0 * 1_000_000.0) as u64 ) ); - engine.register_fn( "m", |value: i64| Duration::from_secs( value as u64 * 60 ) ); - engine.register_fn( "m", |value: f64| Duration::from_usecs( (value * 60.0 * 1_000_000.0) as u64 ) ); - engine.register_fn( "s", |value: i64| Duration::from_secs( value as u64 ) ); - engine.register_fn( "s", |value: f64| Duration::from_secs( (value * 1_000_000.0) as u64 ) ); - engine.register_fn( "ms", |value: i64| Duration::from_msecs( value as u64 ) ); - engine.register_fn( "ms", |value: f64| Duration::from_usecs( (value * 1_000.0) as u64 ) ); - engine.register_fn( "us", |value: i64| Duration::from_usecs( value as u64 ) ); - engine.register_fn( "us", |value: f64| Duration::from_usecs( value as u64 ) ); - engine.register_fn( "*", |lhs: Duration, rhs: i64| -> Duration { Duration( lhs.0 * rhs as f64 ) } ); - engine.register_fn( "*", |lhs: i64, rhs: Duration| -> Duration { Duration( rhs.0 * lhs as f64 ) } ); - engine.register_fn( "*", |lhs: Duration, rhs: f64| -> Duration { Duration( lhs.0 * rhs as f64 ) } ); - engine.register_fn( "*", |lhs: f64, rhs: Duration| -> Duration { Duration( rhs.0 * lhs as f64 ) } ); - engine.register_fn( "+", |lhs: Duration, rhs: Duration| -> Duration { Duration( lhs.0 + rhs.0 ) } ); - engine.register_fn( "-", |lhs: Duration, rhs: Duration| -> Duration { Duration( lhs.0 - rhs.0 ) } ); - engine.register_fn( "kb", |value: i64| value * 1000 ); - engine.register_fn( "mb", |value: i64| value * 1000 * 1000 ); - engine.register_fn( "gb", |value: i64| value * 1000 * 1000 * 1000 ); - engine.register_fn( "info", |message: &str| info!( "{}", message ) ); - engine.register_type::< Duration >(); - engine.register_fn( "argv", move || -> rhai::Array { - argv.iter().cloned().map( rhai::Dynamic::from ).collect() + engine.register_fn("dirname", dirname); + engine.register_fn("h", |value: i64| Duration::from_secs(value as u64 * 3600)); + engine.register_fn("h", |value: f64| { + Duration::from_usecs((value * 3600.0 * 1_000_000.0) as u64) + }); + engine.register_fn("m", |value: i64| Duration::from_secs(value as u64 * 60)); + engine.register_fn("m", |value: f64| { + Duration::from_usecs((value * 60.0 * 1_000_000.0) as u64) + }); + engine.register_fn("s", |value: i64| Duration::from_secs(value as u64)); + engine.register_fn("s", |value: f64| { + Duration::from_secs((value * 1_000_000.0) as u64) + }); + engine.register_fn("ms", |value: i64| Duration::from_msecs(value as u64)); + engine.register_fn("ms", |value: f64| { + Duration::from_usecs((value * 1_000.0) as u64) + }); + engine.register_fn("us", |value: i64| Duration::from_usecs(value as u64)); + engine.register_fn("us", |value: f64| Duration::from_usecs(value as u64)); + engine.register_fn("*", |lhs: Duration, rhs: i64| -> Duration { + Duration(lhs.0 * rhs as f64) + }); + engine.register_fn("*", |lhs: i64, rhs: Duration| -> Duration { + Duration(rhs.0 * lhs as f64) + }); + engine.register_fn("*", |lhs: Duration, rhs: f64| -> Duration { + Duration(lhs.0 * rhs as f64) + }); + engine.register_fn("*", |lhs: f64, rhs: Duration| -> Duration { + Duration(rhs.0 * lhs as f64) + }); + engine.register_fn("+", |lhs: Duration, rhs: Duration| -> Duration { + Duration(lhs.0 + rhs.0) + }); + engine.register_fn("-", |lhs: Duration, rhs: Duration| -> Duration { + Duration(lhs.0 - rhs.0) + }); + engine.register_fn("kb", |value: i64| value * 1000); + engine.register_fn("mb", |value: i64| value * 1000 * 1000); + engine.register_fn("gb", |value: i64| value * 1000 * 1000 * 1000); + engine.register_fn("info", |message: &str| info!("{}", message)); + engine.register_type::(); + engine.register_fn("argv", move || -> rhai::Array { + argv.iter().cloned().map(rhai::Dynamic::from).collect() }); { let env = env.clone(); - engine.register_result_fn( "mkdir_p", move |path: &str| env.lock().mkdir_p( path ) ); + engine.register_result_fn("mkdir_p", move |path: &str| env.lock().mkdir_p(path)); } { let env = env.clone(); - engine.register_result_fn( "chdir", move |path: &str| env.lock().chdir( path ) ); + engine.register_result_fn("chdir", move |path: &str| env.lock().chdir(path)); } { let env = env.clone(); - engine.register_result_fn( "exit", move |errorcode: i64| env.lock().exit( Some( errorcode as i32 ) ) ); + engine.register_result_fn("exit", move |errorcode: i64| { + env.lock().exit(Some(errorcode as i32)) + }); } { let env = env.clone(); - engine.register_result_fn( "exit", move || env.lock().exit( None ) ); + engine.register_result_fn("exit", move || env.lock().exit(None)); } // DSL functions. - engine.register_type::< DataRef >(); - engine.register_type::< Allocation >(); - engine.register_type::< AllocationList >(); - engine.register_type::< AllocationGroupList >(); - engine.register_type::< Map >(); - engine.register_type::< MapList >(); - engine.register_type::< Backtrace >(); - engine.register_type::< Graph >(); - engine.register_fn( "graph", Graph::new ); - engine.register_result_fn( "add", Graph::add ); - engine.register_result_fn( "add", Graph::add_with_label ); - engine.register_result_fn( "add", Graph::add_group ); - engine.register_result_fn( "add", Graph::add_maps ); - engine.register_result_fn( "add", Graph::add_maps_with_label ); - engine.register_fn( "trim_left", Graph::trim_left ); - engine.register_fn( "trim_right", Graph::trim_right ); - engine.register_fn( "trim", Graph::trim ); - engine.register_fn( "start_at", Graph::start_at ); - engine.register_fn( "end_at", Graph::end_at ); - engine.register_fn( "only_non_empty_series", Graph::only_non_empty_series ); - engine.register_fn( "without_legend", Graph::without_legend ); - engine.register_fn( "without_axes", Graph::without_axes ); - engine.register_fn( "without_grid", Graph::without_grid ); - engine.register_result_fn( "show_memory_usage", Graph::show_memory_usage ); - engine.register_result_fn( "show_live_allocations", Graph::show_live_allocations ); - engine.register_result_fn( "show_new_allocations", Graph::show_new_allocations ); - engine.register_result_fn( "show_deallocations", Graph::show_deallocations ); - engine.register_result_fn( "show_rss", Graph::show_rss ); - engine.register_result_fn( "show_address_space", Graph::show_address_space ); - - engine.register_result_fn( "with_gradient_color_scheme", Graph::with_gradient_color_scheme ); - engine.register_fn( "allocations", DataRef::allocations ); - engine.register_fn( "maps", DataRef::maps ); - engine.register_fn( "runtime", |data: &mut DataRef| Duration( data.0.last_timestamp - data.0.initial_timestamp ) ); - - engine.register_fn( "strip", |backtrace: &mut Backtrace| { + engine.register_type::(); + engine.register_type::(); + engine.register_type::(); + engine.register_type::(); + engine.register_type::(); + engine.register_type::(); + engine.register_type::(); + engine.register_type::(); + engine.register_fn("graph", Graph::new); + engine.register_result_fn("add", Graph::add); + engine.register_result_fn("add", Graph::add_with_label); + engine.register_result_fn("add", Graph::add_group); + engine.register_result_fn("add", Graph::add_maps); + engine.register_result_fn("add", Graph::add_maps_with_label); + engine.register_fn("trim_left", Graph::trim_left); + engine.register_fn("trim_right", Graph::trim_right); + engine.register_fn("trim", Graph::trim); + engine.register_fn("start_at", Graph::start_at); + engine.register_fn("end_at", Graph::end_at); + engine.register_fn("only_non_empty_series", Graph::only_non_empty_series); + engine.register_fn("without_legend", Graph::without_legend); + engine.register_fn("without_axes", Graph::without_axes); + engine.register_fn("without_grid", Graph::without_grid); + engine.register_result_fn("show_memory_usage", Graph::show_memory_usage); + engine.register_result_fn("show_live_allocations", Graph::show_live_allocations); + engine.register_result_fn("show_new_allocations", Graph::show_new_allocations); + engine.register_result_fn("show_deallocations", Graph::show_deallocations); + engine.register_result_fn("show_rss", Graph::show_rss); + engine.register_result_fn("show_address_space", Graph::show_address_space); + + engine.register_result_fn( + "with_gradient_color_scheme", + Graph::with_gradient_color_scheme, + ); + engine.register_fn("allocations", DataRef::allocations); + engine.register_fn("maps", DataRef::maps); + engine.register_fn("runtime", |data: &mut DataRef| { + Duration(data.0.last_timestamp - data.0.initial_timestamp) + }); + + engine.register_fn("strip", |backtrace: &mut Backtrace| { let mut cloned = backtrace.clone(); cloned.strip = true; cloned }); - fn set_max< T >( target: &mut Option< T >, value: T ) where T: PartialOrd { - if let Some( target ) = target.as_mut() { + fn set_max(target: &mut Option, value: T) + where + T: PartialOrd, + { + if let Some(target) = target.as_mut() { if *target < value { *target = value; } } else { - *target = Some( value ); + *target = Some(value); } } - fn set_min< T >( target: &mut Option< T >, value: T ) where T: PartialOrd { - if let Some( target ) = target.as_mut() { + fn set_min(target: &mut Option, value: T) + where + T: PartialOrd, + { + if let Some(target) = target.as_mut() { if *target > value { *target = value; } } else { - *target = Some( value ); + *target = Some(value); } } fn gather_backtrace_ids( - set: &mut HashSet< BacktraceId >, - arg: rhai::Dynamic - ) -> Result< (), Box< rhai::EvalAltResult > > { - if let Some( id ) = arg.clone().try_cast::< i64 >() { - set.insert( BacktraceId::new( id as u32 ) ); - } else if let Some( obj ) = arg.clone().try_cast::< Backtrace >() { - set.insert( obj.id ); - } else if let Some( mut obj ) = arg.clone().try_cast::< AllocationList >() { + set: &mut HashSet, + arg: rhai::Dynamic, + ) -> Result<(), Box> { + if let Some(id) = arg.clone().try_cast::() { + set.insert(BacktraceId::new(id as u32)); + } else if let Some(obj) = arg.clone().try_cast::() { + set.insert(obj.id); + } else if let Some(mut obj) = arg.clone().try_cast::() { let data = obj.data.clone(); obj.apply_filter(); - set.par_extend( obj.unfiltered_ids_par_iter().map( |allocation_id| { - let allocation = data.get_allocation( allocation_id ); + set.par_extend(obj.unfiltered_ids_par_iter().map(|allocation_id| { + let allocation = data.get_allocation(allocation_id); allocation.backtrace })); - } else if let Some( mut obj ) = arg.clone().try_cast::< MapList >() { + } else if let Some(mut obj) = arg.clone().try_cast::() { let data = obj.data.clone(); obj.apply_filter(); - set.par_extend( obj.unfiltered_ids_par_iter().flat_map( |id| { - let map = data.get_map( id ); - map.source.map( |source| source.backtrace ) + set.par_extend(obj.unfiltered_ids_par_iter().flat_map(|id| { + let map = data.get_map(id); + map.source.map(|source| source.backtrace) })); - } else if let Some( obj ) = arg.clone().try_cast::< AllocationGroupList >() { + } else if let Some(obj) = arg.clone().try_cast::() { let data = obj.data.clone(); for group in obj.groups.iter() { for allocation_id in group.allocation_ids.iter() { - let allocation = data.get_allocation( *allocation_id ); - set.insert( allocation.backtrace ); + let allocation = data.get_allocation(*allocation_id); + set.insert(allocation.backtrace); } } - } else if let Some( obj ) = arg.clone().try_cast::< rhai::Array >() { + } else if let Some(obj) = arg.clone().try_cast::() { for subobj in obj { - gather_backtrace_ids( set, subobj )?; + gather_backtrace_ids(set, subobj)?; } } else { let error = error( format!( "expected a raw backtrace ID, 'Backtrace' object, 'AllocationList' object, 'MapList' object, or an array of any of them, got {}", arg.type_name() ) ); - return Err( error ); + return Err(error); } Ok(()) } fn gather_map_ids( - set: &mut HashSet< MapId >, - arg: rhai::Dynamic - ) -> Result< (), Box< rhai::EvalAltResult > > { - if let Some( id ) = arg.clone().try_cast::< i64 >() { - set.insert( MapId( id as u64 ) ); - } else if let Some( obj ) = arg.clone().try_cast::< Map >() { - set.insert( obj.id ); - } else if let Some( mut obj ) = arg.clone().try_cast::< MapList >() { + set: &mut HashSet, + arg: rhai::Dynamic, + ) -> Result<(), Box> { + if let Some(id) = arg.clone().try_cast::() { + set.insert(MapId(id as u64)); + } else if let Some(obj) = arg.clone().try_cast::() { + set.insert(obj.id); + } else if let Some(mut obj) = arg.clone().try_cast::() { obj.apply_filter(); - set.par_extend( obj.unfiltered_ids_par_iter() ); - } else if let Some( obj ) = arg.clone().try_cast::< rhai::Array >() { + set.par_extend(obj.unfiltered_ids_par_iter()); + } else if let Some(obj) = arg.clone().try_cast::() { for subobj in obj { - gather_map_ids( set, subobj )?; + gather_map_ids(set, subobj)?; } } else { let error = error( format!( "expected a raw map ID, 'Map' object, 'MapList' object, or an array of any of them, got {}", arg.type_name() ) ); - return Err( error ); + return Err(error); } Ok(()) @@ -2189,39 +2502,39 @@ impl Engine { macro_rules! register_filter { ($ty_name:ident, $setter:ident, $field:ident.$name:ident, $src_ty:ty => $dst_ty:ty) => { - engine.register_fn( stringify!( $name ), |list: &mut $ty_name, value: $src_ty| - list.add_filter( |filter| $setter( &mut filter.$field.$name, value as $dst_ty ) ) - ); + engine.register_fn(stringify!($name), |list: &mut $ty_name, value: $src_ty| { + list.add_filter(|filter| $setter(&mut filter.$field.$name, value as $dst_ty)) + }); }; ($ty_name:ident, $setter:ident, $name:ident, $src_ty:ty => $dst_ty:ty) => { - engine.register_fn( stringify!( $name ), |list: &mut $ty_name, value: $src_ty| - list.add_filter( |filter| $setter( &mut filter.$name, value as $dst_ty ) ) - ); + engine.register_fn(stringify!($name), |list: &mut $ty_name, value: $src_ty| { + list.add_filter(|filter| $setter(&mut filter.$name, value as $dst_ty)) + }); }; ($ty_name:ident, $field:ident.$name:ident, bool) => { - engine.register_fn( stringify!( $name ), |list: &mut $ty_name| - list.add_filter( |filter| filter.$field.$name = true ) - ); + engine.register_fn(stringify!($name), |list: &mut $ty_name| { + list.add_filter(|filter| filter.$field.$name = true) + }); }; ($ty_name:ident, $name:ident, bool) => { - engine.register_fn( stringify!( $name ), |list: &mut $ty_name| - list.add_filter( |filter| filter.$name = true ) - ); + engine.register_fn(stringify!($name), |list: &mut $ty_name| { + list.add_filter(|filter| filter.$name = true) + }); }; ($ty_name:ident, $setter:ident, $field:ident.$name:ident, $ty:ty) => { - engine.register_fn( stringify!( $name ), |list: &mut $ty_name, value: $ty| - list.add_filter( |filter| $setter( &mut filter.$field.$name, value as $ty ) ) - ); + engine.register_fn(stringify!($name), |list: &mut $ty_name, value: $ty| { + list.add_filter(|filter| $setter(&mut filter.$field.$name, value as $ty)) + }); }; ($ty_name:ident, $setter:ident, $name:ident, $ty:ty) => { - engine.register_fn( stringify!( $name ), |list: &mut $ty_name, value: $ty| - list.add_filter( |filter| $setter( &mut filter.$name, value as $ty ) ) - ); + engine.register_fn(stringify!($name), |list: &mut $ty_name, value: $ty| { + list.add_filter(|filter| $setter(&mut filter.$name, value as $ty)) + }); }; } @@ -2235,130 +2548,220 @@ impl Engine { register_filter!( AllocationList, set_min, only_last_size_smaller, i64 => u64 ); register_filter!( AllocationList, set_max, only_chain_length_at_least, i64 => u32 ); register_filter!( AllocationList, set_min, only_chain_length_at_most, i64 => u32 ); - register_filter!( AllocationList, set_max, only_chain_alive_for_at_least, Duration ); - register_filter!( AllocationList, set_min, only_chain_alive_for_at_most, Duration ); + register_filter!( + AllocationList, + set_max, + only_chain_alive_for_at_least, + Duration + ); + register_filter!( + AllocationList, + set_min, + only_chain_alive_for_at_most, + Duration + ); register_filter!( AllocationList, set_max, only_position_in_chain_at_least, i64 => u32 ); register_filter!( AllocationList, set_min, only_position_in_chain_at_most, i64 => u32 ); register_filter!( AllocationList, set_max, only_group_allocations_at_least, i64 => usize ); register_filter!( AllocationList, set_min, only_group_allocations_at_most, i64 => usize ); - register_filter!( AllocationList, set_max, only_group_interval_at_least, Duration ); - register_filter!( AllocationList, set_min, only_group_interval_at_most, Duration ); - register_filter!( AllocationList, set_max, only_group_max_total_usage_first_seen_at_least, Duration ); - register_filter!( AllocationList, set_min, only_group_max_total_usage_first_seen_at_most, Duration ); - - engine.register_fn( "only_group_leaked_allocations_at_least", |list: &mut AllocationList, value: f64| { - list.add_filter_once( |filter| filter.only_group_leaked_allocations_at_least.is_some(), |filter| - filter.only_group_leaked_allocations_at_least = Some( NumberOrFractionOfTotal::Fraction( value ) ) - ) - }); - engine.register_fn( "only_group_leaked_allocations_at_least", |list: &mut AllocationList, value: i64| { - list.add_filter_once( |filter| filter.only_group_leaked_allocations_at_least.is_some(), |filter| - filter.only_group_leaked_allocations_at_least = Some( NumberOrFractionOfTotal::Number( value as u64 ) ) - ) - }); - engine.register_fn( "only_group_leaked_allocations_at_most", |list: &mut AllocationList, value: f64| { - list.add_filter_once( |filter| filter.only_group_leaked_allocations_at_most.is_some(), |filter| - filter.only_group_leaked_allocations_at_most = Some( NumberOrFractionOfTotal::Fraction( value ) ) - ) - }); - engine.register_fn( "only_group_leaked_allocations_at_most", |list: &mut AllocationList, value: i64| { - list.add_filter_once( |filter| filter.only_group_leaked_allocations_at_most.is_some(), |filter| - filter.only_group_leaked_allocations_at_most = Some( NumberOrFractionOfTotal::Number( value as u64 ) ) - ) - }); + register_filter!( + AllocationList, + set_max, + only_group_interval_at_least, + Duration + ); + register_filter!( + AllocationList, + set_min, + only_group_interval_at_most, + Duration + ); + register_filter!( + AllocationList, + set_max, + only_group_max_total_usage_first_seen_at_least, + Duration + ); + register_filter!( + AllocationList, + set_min, + only_group_max_total_usage_first_seen_at_most, + Duration + ); - register_filter!( AllocationList, only_chain_leaked, bool ); - register_filter!( AllocationList, only_ptmalloc_mmaped, bool ); - register_filter!( AllocationList, only_ptmalloc_not_mmaped, bool ); - register_filter!( AllocationList, only_ptmalloc_from_main_arena, bool ); - register_filter!( AllocationList, only_ptmalloc_not_from_main_arena, bool ); - register_filter!( AllocationList, only_jemalloc, bool ); - register_filter!( AllocationList, only_not_jemalloc, bool ); - - engine.register_fn( "only_with_marker", |list: &mut AllocationList, value: i64| { - list.add_filter_once( |filter| filter.only_with_marker.is_some(), |filter| - filter.only_with_marker = Some( value as u32 ) - ) - }); + engine.register_fn( + "only_group_leaked_allocations_at_least", + |list: &mut AllocationList, value: f64| { + list.add_filter_once( + |filter| filter.only_group_leaked_allocations_at_least.is_some(), + |filter| { + filter.only_group_leaked_allocations_at_least = + Some(NumberOrFractionOfTotal::Fraction(value)) + }, + ) + }, + ); + engine.register_fn( + "only_group_leaked_allocations_at_least", + |list: &mut AllocationList, value: i64| { + list.add_filter_once( + |filter| filter.only_group_leaked_allocations_at_least.is_some(), + |filter| { + filter.only_group_leaked_allocations_at_least = + Some(NumberOrFractionOfTotal::Number(value as u64)) + }, + ) + }, + ); + engine.register_fn( + "only_group_leaked_allocations_at_most", + |list: &mut AllocationList, value: f64| { + list.add_filter_once( + |filter| filter.only_group_leaked_allocations_at_most.is_some(), + |filter| { + filter.only_group_leaked_allocations_at_most = + Some(NumberOrFractionOfTotal::Fraction(value)) + }, + ) + }, + ); + engine.register_fn( + "only_group_leaked_allocations_at_most", + |list: &mut AllocationList, value: i64| { + list.add_filter_once( + |filter| filter.only_group_leaked_allocations_at_most.is_some(), + |filter| { + filter.only_group_leaked_allocations_at_most = + Some(NumberOrFractionOfTotal::Number(value as u64)) + }, + ) + }, + ); - engine.register_fn( "group_by_backtrace", AllocationList::group_by_backtrace ); - - engine.register_fn( "only_all_leaked", AllocationGroupList::only_all_leaked ); - engine.register_fn( "only_count_at_least", AllocationGroupList::only_count_at_least ); - engine.register_fn( "len", AllocationGroupList::len ); - engine.register_fn( "sort_by_size_ascending", AllocationGroupList::sort_by_size_ascending ); - engine.register_fn( "sort_by_size_descending", AllocationGroupList::sort_by_size_descending ); - engine.register_fn( "sort_by_size", AllocationGroupList::sort_by_size_descending ); - engine.register_fn( "sort_by_count_ascending", AllocationGroupList::sort_by_count_ascending ); - engine.register_fn( "sort_by_count_descending", AllocationGroupList::sort_by_count_descending ); - engine.register_fn( "sort_by_count", AllocationGroupList::sort_by_count_descending ); - engine.register_fn( "ungroup", AllocationGroupList::ungroup ); - engine.register_indexer_get_result( AllocationGroupList::get ); - engine.register_fn( "take", AllocationGroupList::take ); - engine.register_iterator::< AllocationGroupList >(); - - engine.register_fn( "backtrace", |allocation: &mut Allocation| { - Backtrace { - data: allocation.data.clone(), - id: allocation.data.get_allocation( allocation.id ).backtrace, - strip: false - } + register_filter!(AllocationList, only_chain_leaked, bool); + register_filter!(AllocationList, only_ptmalloc_mmaped, bool); + register_filter!(AllocationList, only_ptmalloc_not_mmaped, bool); + register_filter!(AllocationList, only_ptmalloc_from_main_arena, bool); + register_filter!(AllocationList, only_ptmalloc_not_from_main_arena, bool); + register_filter!(AllocationList, only_jemalloc, bool); + register_filter!(AllocationList, only_not_jemalloc, bool); + + engine.register_fn( + "only_with_marker", + |list: &mut AllocationList, value: i64| { + list.add_filter_once( + |filter| filter.only_with_marker.is_some(), + |filter| filter.only_with_marker = Some(value as u32), + ) + }, + ); + + engine.register_fn("group_by_backtrace", AllocationList::group_by_backtrace); + + engine.register_fn("only_all_leaked", AllocationGroupList::only_all_leaked); + engine.register_fn( + "only_count_at_least", + AllocationGroupList::only_count_at_least, + ); + engine.register_fn("len", AllocationGroupList::len); + engine.register_fn( + "sort_by_size_ascending", + AllocationGroupList::sort_by_size_ascending, + ); + engine.register_fn( + "sort_by_size_descending", + AllocationGroupList::sort_by_size_descending, + ); + engine.register_fn("sort_by_size", AllocationGroupList::sort_by_size_descending); + engine.register_fn( + "sort_by_count_ascending", + AllocationGroupList::sort_by_count_ascending, + ); + engine.register_fn( + "sort_by_count_descending", + AllocationGroupList::sort_by_count_descending, + ); + engine.register_fn( + "sort_by_count", + AllocationGroupList::sort_by_count_descending, + ); + engine.register_fn("ungroup", AllocationGroupList::ungroup); + engine.register_indexer_get_result(AllocationGroupList::get); + engine.register_fn("take", AllocationGroupList::take); + engine.register_iterator::(); + + engine.register_fn("backtrace", |allocation: &mut Allocation| Backtrace { + data: allocation.data.clone(), + id: allocation.data.get_allocation(allocation.id).backtrace, + strip: false, }); - engine.register_fn( "backtrace", |map: &mut Map| { - map.data.get_map( map.id ).source.map( |source| { - Backtrace { - data: map.data.clone(), - id: source.backtrace, - strip: false - } + engine.register_fn("backtrace", |map: &mut Map| { + map.data.get_map(map.id).source.map(|source| Backtrace { + data: map.data.clone(), + id: source.backtrace, + strip: false, }) }); - engine.register_fn( "allocated_at", |allocation: &mut Allocation| { - Duration( allocation.data.get_allocation( allocation.id ).timestamp - allocation.data.initial_timestamp ) + engine.register_fn("allocated_at", |allocation: &mut Allocation| { + Duration( + allocation.data.get_allocation(allocation.id).timestamp + - allocation.data.initial_timestamp, + ) }); - engine.register_fn( "allocated_at", |map: &mut Map| { - Duration( map.data.get_map( map.id ).timestamp - map.data.initial_timestamp ) + engine.register_fn("allocated_at", |map: &mut Map| { + Duration(map.data.get_map(map.id).timestamp - map.data.initial_timestamp) }); - engine.register_fn( "deallocated_at", |allocation: &mut Allocation| { - Some( Duration( allocation.data.get_allocation( allocation.id ).deallocation.as_ref()?.timestamp - allocation.data.initial_timestamp ) ) + engine.register_fn("deallocated_at", |allocation: &mut Allocation| { + Some(Duration( + allocation + .data + .get_allocation(allocation.id) + .deallocation + .as_ref()? + .timestamp + - allocation.data.initial_timestamp, + )) }); - engine.register_fn( "deallocated_at", |map: &mut Map| { - Some( Duration( map.data.get_map( map.id ).deallocation.as_ref()?.timestamp - map.data.initial_timestamp ) ) + engine.register_fn("deallocated_at", |map: &mut Map| { + Some(Duration( + map.data.get_map(map.id).deallocation.as_ref()?.timestamp + - map.data.initial_timestamp, + )) }); register_filter!( MapList, set_max, only_peak_rss_at_least, i64 => u64 ); register_filter!( MapList, set_min, only_peak_rss_at_most, i64 => u64 ); - register_filter!( MapList, only_jemalloc, bool ); - register_filter!( MapList, only_not_jemalloc, bool ); - register_filter!( MapList, only_bytehound, bool ); - register_filter!( MapList, only_not_bytehound, bool ); - register_filter!( MapList, only_readable, bool ); - register_filter!( MapList, only_not_readable, bool ); - register_filter!( MapList, only_writable, bool ); - register_filter!( MapList, only_not_writable, bool ); - register_filter!( MapList, only_executable, bool ); - register_filter!( MapList, only_not_executable, bool ); + register_filter!(MapList, only_jemalloc, bool); + register_filter!(MapList, only_not_jemalloc, bool); + register_filter!(MapList, only_bytehound, bool); + register_filter!(MapList, only_not_bytehound, bool); + register_filter!(MapList, only_readable, bool); + register_filter!(MapList, only_not_readable, bool); + register_filter!(MapList, only_writable, bool); + register_filter!(MapList, only_not_writable, bool); + register_filter!(MapList, only_executable, bool); + register_filter!(MapList, only_not_executable, bool); - let graph_counter = Arc::new( AtomicUsize::new( 1 ) ); - let flamegraph_counter = Arc::new( AtomicUsize::new( 1 ) ); + let graph_counter = Arc::new(AtomicUsize::new(1)); + let flamegraph_counter = Arc::new(AtomicUsize::new(1)); - fn get_counter( graph_counter: &AtomicUsize ) -> usize { - graph_counter.fetch_add( 1, std::sync::atomic::Ordering::SeqCst ) + fn get_counter(graph_counter: &AtomicUsize) -> usize { + graph_counter.fetch_add(1, std::sync::atomic::Ordering::SeqCst) } { let data = args.data.clone(); - engine.register_result_fn( "data", move || { - if let Some( ref data ) = data { - Ok( DataRef( data.clone() ) ) + engine.register_result_fn("data", move || { + if let Some(ref data) = data { + Ok(DataRef(data.clone())) } else { - Err( error( "no globally loaded data file" ) ) + Err(error("no globally loaded data file")) } }); } @@ -2366,15 +2769,15 @@ impl Engine { { let data = args.data.clone(); let allocation_ids = args.allocation_ids.clone(); - engine.register_result_fn( "allocations", move || { - if let Some( ref data ) = data { - Ok( AllocationList { - data: DataRef( data.clone() ), + engine.register_result_fn("allocations", move || { + if let Some(ref data) = data { + Ok(AllocationList { + data: DataRef(data.clone()), allocation_ids: allocation_ids.clone(), - filter: None + filter: None, }) } else { - Err( error( "no globally loaded allocations" ) ) + Err(error("no globally loaded allocations")) } }); } @@ -2382,116 +2785,129 @@ impl Engine { { let data = args.data.clone(); let map_ids = args.map_ids.clone(); - engine.register_result_fn( "maps", move || { - if let Some( ref data ) = data { - Ok( MapList { - data: DataRef( data.clone() ), + engine.register_result_fn("maps", move || { + if let Some(ref data) = data { + Ok(MapList { + data: DataRef(data.clone()), map_ids: map_ids.clone(), - filter: None + filter: None, }) } else { - Err( error( "no globally loaded maps" ) ) + Err(error("no globally loaded maps")) } }); } { let env = env.clone(); - engine.register_result_fn( "load", move |path: String| Ok( DataRef( env.lock().load( path )? ) ) ); + engine.register_result_fn("load", move |path: String| { + Ok(DataRef(env.lock().load(path)?)) + }); } { let env = env.clone(); - engine.register_result_fn( - "save", - move |graph: &mut Graph, path: String| Graph::save( graph, &mut *env.lock(), path ) - ); + engine.register_result_fn("save", move |graph: &mut Graph, path: String| { + Graph::save(graph, &mut *env.lock(), path) + }); } { let env = env.clone(); let graph_counter = graph_counter.clone(); - engine.register_result_fn( - "save", - move |graph: &mut Graph| Graph::save( graph, &mut *env.lock(), format!( "Graph #{}.svg", get_counter( &graph_counter ) ) ) - ); + engine.register_result_fn("save", move |graph: &mut Graph| { + Graph::save( + graph, + &mut *env.lock(), + format!("Graph #{}.svg", get_counter(&graph_counter)), + ) + }); } { let env = env.clone(); engine.register_result_fn( "save_each_series_as_graph", - move |graph: &mut Graph, path: String| Graph::save_each_series_as_graph( graph, &mut *env.lock(), path ) + move |graph: &mut Graph, path: String| { + Graph::save_each_series_as_graph(graph, &mut *env.lock(), path) + }, ); } { let env = env.clone(); - engine.register_result_fn( - "save_each_series_as_graph", - move |graph: &mut Graph| Graph::save_each_series_as_graph( graph, &mut *env.lock(), ".".into() ) - ); + engine.register_result_fn("save_each_series_as_graph", move |graph: &mut Graph| { + Graph::save_each_series_as_graph(graph, &mut *env.lock(), ".".into()) + }); } { let env = env.clone(); engine.register_result_fn( "save_each_series_as_flamegraph", - move |graph: &mut Graph, path: String| Graph::save_each_series_as_flamegraph( graph, &mut *env.lock(), path ) + move |graph: &mut Graph, path: String| { + Graph::save_each_series_as_flamegraph(graph, &mut *env.lock(), path) + }, ); } { let env = env.clone(); engine.register_result_fn( "save_each_series_as_flamegraph", - move |graph: &mut Graph| Graph::save_each_series_as_flamegraph( graph, &mut *env.lock(), ".".into() ) + move |graph: &mut Graph| { + Graph::save_each_series_as_flamegraph(graph, &mut *env.lock(), ".".into()) + }, ); } { let env = env.clone(); engine.register_result_fn( "save_as_flamegraph", - move |list: &mut AllocationList, path: String| AllocationList::save_as_flamegraph( list, &mut *env.lock(), path ) + move |list: &mut AllocationList, path: String| { + AllocationList::save_as_flamegraph(list, &mut *env.lock(), path) + }, ); } { let env = env.clone(); let flamegraph_counter = flamegraph_counter.clone(); - engine.register_result_fn( - "save_as_flamegraph", - move |list: &mut AllocationList| AllocationList::save_as_flamegraph( list, &mut *env.lock(), format!( "Flamegraph #{}.svg", get_counter( &flamegraph_counter ) ) ) - ); + engine.register_result_fn("save_as_flamegraph", move |list: &mut AllocationList| { + AllocationList::save_as_flamegraph( + list, + &mut *env.lock(), + format!("Flamegraph #{}.svg", get_counter(&flamegraph_counter)), + ) + }); } { let env = env.clone(); engine.register_result_fn( "save_as_graph", - move |list: &mut AllocationList, path: String| AllocationList::save_as_graph( list, &mut *env.lock(), path ) + move |list: &mut AllocationList, path: String| { + AllocationList::save_as_graph(list, &mut *env.lock(), path) + }, ); } { let env = env.clone(); let graph_counter = graph_counter.clone(); - engine.register_result_fn( - "save_as_graph", - move |list: &mut AllocationList| AllocationList::save_as_graph( list, &mut *env.lock(), format!( "Graph #{}.svg", get_counter( &graph_counter ) ) ) - ); + engine.register_result_fn("save_as_graph", move |list: &mut AllocationList| { + AllocationList::save_as_graph( + list, + &mut *env.lock(), + format!("Graph #{}.svg", get_counter(&graph_counter)), + ) + }); } { let env = env.clone(); - engine.register_fn( - "println", - move || { - env.lock().println( "" ); - } - ); + engine.register_fn("println", move || { + env.lock().println(""); + }); } { let env = env.clone(); - engine.register_fn( - "println", - move |a0: rhai::plugin::Dynamic| { - env.lock().println( &to_string( a0 ) ); - } - ); + engine.register_fn("println", move |a0: rhai::plugin::Dynamic| { + env.lock().println(&to_string(a0)); + }); } { @@ -2499,12 +2915,12 @@ impl Engine { engine.register_result_fn( "println", move |a0: rhai::plugin::Dynamic, a1: rhai::plugin::Dynamic| { - let a0 = to_string( a0 ); - let a1 = to_string( a1 ); - let message = format( &a0, &[&a1] )?; - env.lock().println( &message ); + let a0 = to_string(a0); + let a1 = to_string(a1); + let message = format(&a0, &[&a1])?; + env.lock().println(&message); Ok(()) - } + }, ); } @@ -2512,14 +2928,16 @@ impl Engine { let env = env.clone(); engine.register_result_fn( "println", - move |a0: rhai::plugin::Dynamic, a1: rhai::plugin::Dynamic, a2: rhai::plugin::Dynamic| { - let a0 = to_string( a0 ); - let a1 = to_string( a1 ); - let a2 = to_string( a2 ); - let message = format( &a0, &[&a1, &a2] )?; - env.lock().println( &message ); + move |a0: rhai::plugin::Dynamic, + a1: rhai::plugin::Dynamic, + a2: rhai::plugin::Dynamic| { + let a0 = to_string(a0); + let a1 = to_string(a1); + let a2 = to_string(a2); + let message = format(&a0, &[&a1, &a2])?; + env.lock().println(&message); Ok(()) - } + }, ); } @@ -2527,15 +2945,18 @@ impl Engine { let env = env.clone(); engine.register_result_fn( "println", - move |a0: rhai::plugin::Dynamic, a1: rhai::plugin::Dynamic, a2: rhai::plugin::Dynamic, a3: rhai::plugin::Dynamic| { - let a0 = to_string( a0 ); - let a1 = to_string( a1 ); - let a2 = to_string( a2 ); - let a3 = to_string( a3 ); - let message = format( &a0, &[&a1, &a2, &a3] )?; - env.lock().println( &message ); + move |a0: rhai::plugin::Dynamic, + a1: rhai::plugin::Dynamic, + a2: rhai::plugin::Dynamic, + a3: rhai::plugin::Dynamic| { + let a0 = to_string(a0); + let a1 = to_string(a1); + let a2 = to_string(a2); + let a3 = to_string(a3); + let message = format(&a0, &[&a1, &a2, &a3])?; + env.lock().println(&message); Ok(()) - } + }, ); } @@ -2661,44 +3082,47 @@ impl Engine { }}; } - engine.register_result_fn( "only_from_maps", |list: &mut AllocationList, ids: rhai::Dynamic| { - let mut set = HashSet::new(); - gather_map_ids( &mut set, ids )?; + engine.register_result_fn( + "only_from_maps", + |list: &mut AllocationList, ids: rhai::Dynamic| { + let mut set = HashSet::new(); + gather_map_ids(&mut set, ids)?; - Ok( list.add_filter( |filter| { - if let Some( ref mut existing ) = filter.only_from_maps { - *existing = existing.intersection( &set ).copied().collect(); - } else { - filter.only_from_maps = Some( set ); - } - })) - }); + Ok(list.add_filter(|filter| { + if let Some(ref mut existing) = filter.only_from_maps { + *existing = existing.intersection(&set).copied().collect(); + } else { + filter.only_from_maps = Some(set); + } + })) + }, + ); - register_list!( AllocationList ); - register_list!( MapList ); + register_list!(AllocationList); + register_list!(MapList); - Engine { - inner: engine - } + Engine { inner: engine } } - pub fn run( &self, code: &str ) -> Result< Option< EvalOutput >, EvalError > { - match self.inner.eval::< rhai::plugin::Dynamic >( code ) { - Ok( value ) => { - if value.is::< AllocationList >() { - Ok( Some( EvalOutput::AllocationList( value.cast::< AllocationList >() ) ) ) - } else if value.is::< MapList >() { - Ok( Some( EvalOutput::MapList( value.cast::< MapList >() ) ) ) + pub fn run(&self, code: &str) -> Result, EvalError> { + match self.inner.eval::(code) { + Ok(value) => { + if value.is::() { + Ok(Some(EvalOutput::AllocationList( + value.cast::(), + ))) + } else if value.is::() { + Ok(Some(EvalOutput::MapList(value.cast::()))) } else { - Ok( None ) + Ok(None) } - }, - Err( error ) => { + } + Err(error) => { let p = error.position(); - Err( EvalError { + Err(EvalError { message: error.to_string(), line: p.line(), - column: p.position() + column: p.position(), }) } } @@ -2706,74 +3130,85 @@ impl Engine { } pub enum EvalOutput { - AllocationList( AllocationList ), - MapList( MapList ) + AllocationList(AllocationList), + MapList(MapList), } #[derive(Debug)] pub struct EvalError { pub message: String, - pub line: Option< usize >, - pub column: Option< usize > + pub line: Option, + pub column: Option, } -impl From< String > for EvalError { - fn from( message: String ) -> Self { - EvalError { message, line: None, column: None } +impl From for EvalError { + fn from(message: String) -> Self { + EvalError { + message, + line: None, + column: None, + } } } -impl< 'a > From< &'a str > for EvalError { - fn from( message: &'a str ) -> Self { +impl<'a> From<&'a str> for EvalError { + fn from(message: &'a str) -> Self { message.to_owned().into() } } -pub fn run_script( path: &Path, data_path: Option< &Path >, argv: Vec< String > ) -> Result< (), std::io::Error > { +pub fn run_script( + path: &Path, + data_path: Option<&Path>, + argv: Vec, +) -> Result<(), std::io::Error> { let mut args = EngineArgs { argv, - .. EngineArgs::default() + ..EngineArgs::default() }; - if let Some( data_path ) = data_path { - info!( "Loading {:?}...", data_path ); - let fp = File::open( &data_path )?; + if let Some(data_path) = data_path { + info!("Loading {:?}...", data_path); + let fp = File::open(&data_path)?; let debug_symbols: &[PathBuf] = &[]; - let data = Loader::load_from_stream( fp, debug_symbols )?; - args.data = Some( Arc::new( data ) ); + let data = Loader::load_from_stream(fp, debug_symbols)?; + args.data = Some(Arc::new(data)); } - let env = Arc::new( Mutex::new( NativeEnvironment::default() ) ); - let engine = Engine::new( env, args ); + let env = Arc::new(Mutex::new(NativeEnvironment::default())); + let engine = Engine::new(env, args); - info!( "Running {:?}...", path ); - let result = engine.inner.eval_file::< rhai::plugin::Dynamic >( path.into() ); + info!("Running {:?}...", path); + let result = engine.inner.eval_file::(path.into()); match result { - Ok( _ ) => {}, - Err( error ) => { - error!( "{}", error ); - return Err( std::io::Error::new( std::io::ErrorKind::Other, "Failed to evaluate the script" ) ); + Ok(_) => {} + Err(error) => { + error!("{}", error); + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "Failed to evaluate the script", + )); } } Ok(()) } -pub fn run_script_slave( data_path: Option< &Path > ) -> Result< (), std::io::Error > { +pub fn run_script_slave(data_path: Option<&Path>) -> Result<(), std::io::Error> { let mut args = EngineArgs::default(); - if let Some( data_path ) = data_path { - info!( "Loading {:?}...", data_path ); - let fp = File::open( &data_path )?; + if let Some(data_path) = data_path { + info!("Loading {:?}...", data_path); + let fp = File::open(&data_path)?; let debug_symbols: &[PathBuf] = &[]; - let data = Loader::load_from_stream( fp, debug_symbols )?; - args.data = Some( Arc::new( data ) ); + let data = Loader::load_from_stream(fp, debug_symbols)?; + args.data = Some(Arc::new(data)); } - let env = Arc::new( Mutex::new( VirtualEnvironment::new() ) ); - let engine = Engine::new( env.clone(), args ); + let env = Arc::new(Mutex::new(VirtualEnvironment::new())); + let engine = Engine::new(env.clone(), args); let mut scope = rhai::Scope::new(); let mut global_ast: rhai::AST = Default::default(); @@ -2784,56 +3219,58 @@ pub fn run_script_slave( data_path: Option< &Path > ) -> Result< (), std::io::Er use std::io::BufRead; buffer.clear(); - match stdin.read_until( 0, &mut buffer ) { - Ok( count ) => { + match stdin.read_until(0, &mut buffer) { + Ok(count) => { if count == 0 { return Ok(()); } - }, - Err( _ ) => return Ok(()) + } + Err(_) => return Ok(()), } - if buffer.ends_with( b"\0" ) { + if buffer.ends_with(b"\0") { buffer.pop(); } - let input = match std::str::from_utf8( &buffer ) { - Ok( input ) => input, - Err( _ ) => { + let input = match std::str::from_utf8(&buffer) { + Ok(input) => input, + Err(_) => { let payload = serde_json::json! {{ "kind": "syntax_error", "message": "invalid utf-8" }}; - println!( "{}", serde_json::to_string( &payload ).unwrap() ); + println!("{}", serde_json::to_string(&payload).unwrap()); let payload = serde_json::json! {{ "kind": "idle" }}; - println!( "{}", serde_json::to_string( &payload ).unwrap() ); + println!("{}", serde_json::to_string(&payload).unwrap()); continue; } }; - match engine.inner.compile_with_scope( &scope, &input ) { - Ok( ast ) => { + match engine.inner.compile_with_scope(&scope, &input) { + Ok(ast) => { global_ast += ast; - let result = engine.inner.eval_ast_with_scope::< rhai::Dynamic >( &mut scope, &global_ast ); + let result = engine + .inner + .eval_ast_with_scope::(&mut scope, &global_ast); global_ast.clear_statements(); - let output = std::mem::take( &mut env.lock().output ); + let output = std::mem::take(&mut env.lock().output); for entry in output { match entry { - ScriptOutputKind::PrintLine( message ) => { + ScriptOutputKind::PrintLine(message) => { let payload = serde_json::json! {{ "kind": "println", "message": message, }}; - println!( "{}", serde_json::to_string( &payload ).unwrap() ); - }, + println!("{}", serde_json::to_string(&payload).unwrap()); + } ScriptOutputKind::Image { path, data } => { let payload = serde_json::json! {{ "kind": "image", @@ -2841,12 +3278,12 @@ pub fn run_script_slave( data_path: Option< &Path > ) -> Result< (), std::io::Er "data": &data[..] }}; - println!( "{}", serde_json::to_string( &payload ).unwrap() ); + println!("{}", serde_json::to_string(&payload).unwrap()); } } } - if let Err( error ) = result { + if let Err(error) = result { let p = error.position(); let payload = serde_json::json! {{ "kind": "runtime_error", @@ -2855,10 +3292,10 @@ pub fn run_script_slave( data_path: Option< &Path > ) -> Result< (), std::io::Er "column": p.position() }}; - println!( "{}", serde_json::to_string( &payload ).unwrap() ); + println!("{}", serde_json::to_string(&payload).unwrap()); } - }, - Err( error ) => { + } + Err(error) => { let p = error.1; let payload = serde_json::json! {{ "kind": "syntax_error", @@ -2867,7 +3304,7 @@ pub fn run_script_slave( data_path: Option< &Path > ) -> Result< (), std::io::Er "column": p.position() }}; - println!( "{}", serde_json::to_string( &payload ).unwrap() ); + println!("{}", serde_json::to_string(&payload).unwrap()); } } @@ -2875,114 +3312,117 @@ pub fn run_script_slave( data_path: Option< &Path > ) -> Result< (), std::io::Er "kind": "idle" }}; - println!( "{}", serde_json::to_string( &payload ).unwrap() ); + println!("{}", serde_json::to_string(&payload).unwrap()); } } struct ToCodeContext { list_source: String, - output: String + output: String, } impl AllocationFilter { - pub fn to_code( &self, list_source: Option< String > ) -> String { + pub fn to_code(&self, list_source: Option) -> String { let mut ctx = ToCodeContext { - list_source: list_source.unwrap_or_else( || "allocations()".into() ), - output: String::new() + list_source: list_source.unwrap_or_else(|| "allocations()".into()), + output: String::new(), }; - self.to_code_impl( &mut ctx ); + self.to_code_impl(&mut ctx); ctx.output } } impl MapFilter { - pub fn to_code( &self, list_source: Option< String > ) -> String { + pub fn to_code(&self, list_source: Option) -> String { let mut ctx = ToCodeContext { - list_source: list_source.unwrap_or_else( || "maps()".into() ), - output: String::new() + list_source: list_source.unwrap_or_else(|| "maps()".into()), + output: String::new(), }; - self.to_code_impl( &mut ctx ); + self.to_code_impl(&mut ctx); ctx.output } } trait ToCode { - fn to_code_impl( &self, ctx: &mut ToCodeContext ); + fn to_code_impl(&self, ctx: &mut ToCodeContext); } -impl< T > ToCode for Filter< T > where T: ToCode { - fn to_code_impl( &self, ctx: &mut ToCodeContext ) { +impl ToCode for Filter +where + T: ToCode, +{ + fn to_code_impl(&self, ctx: &mut ToCodeContext) { match *self { - Filter::Basic( ref filter ) => filter.to_code_impl( ctx ), - Filter::And( ref lhs, ref rhs ) => { - write!( &mut ctx.output, "(" ).unwrap(); - lhs.to_code_impl( ctx ); - write!( &mut ctx.output, " & " ).unwrap(); - rhs.to_code_impl( ctx ); - write!( &mut ctx.output, ")" ).unwrap(); - }, - Filter::Or( ref lhs, ref rhs ) => { - write!( &mut ctx.output, "(" ).unwrap(); - lhs.to_code_impl( ctx ); - write!( &mut ctx.output, " | " ).unwrap(); - rhs.to_code_impl( ctx ); - write!( &mut ctx.output, ")" ).unwrap(); - }, - Filter::Not( ref filter ) => { - write!( &mut ctx.output, "(!" ).unwrap(); - filter.to_code_impl( ctx ); - write!( &mut ctx.output, ")" ).unwrap(); + Filter::Basic(ref filter) => filter.to_code_impl(ctx), + Filter::And(ref lhs, ref rhs) => { + write!(&mut ctx.output, "(").unwrap(); + lhs.to_code_impl(ctx); + write!(&mut ctx.output, " & ").unwrap(); + rhs.to_code_impl(ctx); + write!(&mut ctx.output, ")").unwrap(); + } + Filter::Or(ref lhs, ref rhs) => { + write!(&mut ctx.output, "(").unwrap(); + lhs.to_code_impl(ctx); + write!(&mut ctx.output, " | ").unwrap(); + rhs.to_code_impl(ctx); + write!(&mut ctx.output, ")").unwrap(); + } + Filter::Not(ref filter) => { + write!(&mut ctx.output, "(!").unwrap(); + filter.to_code_impl(ctx); + write!(&mut ctx.output, ")").unwrap(); } } } } impl ToCode for Regex { - fn to_code_impl( &self, ctx: &mut ToCodeContext ) { + fn to_code_impl(&self, ctx: &mut ToCodeContext) { // TODO: Escape the string. - write!( &mut ctx.output, "\"{}\"", self.as_str() ).unwrap(); + write!(&mut ctx.output, "\"{}\"", self.as_str()).unwrap(); } } impl ToCode for u32 { - fn to_code_impl( &self, ctx: &mut ToCodeContext ) { - write!( &mut ctx.output, "{}", self ).unwrap(); + fn to_code_impl(&self, ctx: &mut ToCodeContext) { + write!(&mut ctx.output, "{}", self).unwrap(); } } impl ToCode for u64 { - fn to_code_impl( &self, ctx: &mut ToCodeContext ) { - write!( &mut ctx.output, "{}", self ).unwrap(); + fn to_code_impl(&self, ctx: &mut ToCodeContext) { + write!(&mut ctx.output, "{}", self).unwrap(); } } impl ToCode for usize { - fn to_code_impl( &self, ctx: &mut ToCodeContext ) { - write!( &mut ctx.output, "{}", self ).unwrap(); + fn to_code_impl(&self, ctx: &mut ToCodeContext) { + write!(&mut ctx.output, "{}", self).unwrap(); } } impl ToCode for NumberOrFractionOfTotal { - fn to_code_impl( &self, ctx: &mut ToCodeContext ) { + fn to_code_impl(&self, ctx: &mut ToCodeContext) { match *self { - NumberOrFractionOfTotal::Number( value ) => { - write!( &mut ctx.output, "{}", value ).unwrap(); - }, - NumberOrFractionOfTotal::Fraction( value ) => { - write!( &mut ctx.output, "{}", value ).unwrap(); + NumberOrFractionOfTotal::Number(value) => { + write!(&mut ctx.output, "{}", value).unwrap(); + } + NumberOrFractionOfTotal::Fraction(value) => { + write!(&mut ctx.output, "{}", value).unwrap(); } } } } impl ToCode for Duration { - fn to_code_impl( &self, ctx: &mut ToCodeContext ) { + fn to_code_impl(&self, ctx: &mut ToCodeContext) { if self.0.as_usecs() == 0 { - write!( &mut ctx.output, "s(0)" ).unwrap(); + write!(&mut ctx.output, "s(0)").unwrap(); return; } @@ -2992,67 +3432,67 @@ impl ToCode for Duration { let mut non_empty = false; if d.hours > 0 { non_empty = true; - write!( &mut ctx.output, "h({})", d.hours ).unwrap(); + write!(&mut ctx.output, "h({})", d.hours).unwrap(); } if d.minutes > 0 { if non_empty { - ctx.output.push_str( " + " ); + ctx.output.push_str(" + "); } non_empty = true; - write!( &mut ctx.output, "m({})", d.minutes ).unwrap(); + write!(&mut ctx.output, "m({})", d.minutes).unwrap(); } if d.secs > 0 { if non_empty { - ctx.output.push_str( " + " ); + ctx.output.push_str(" + "); } non_empty = true; - write!( &mut ctx.output, "s({})", d.secs ).unwrap(); + write!(&mut ctx.output, "s({})", d.secs).unwrap(); } if d.ms > 0 { if non_empty { - ctx.output.push_str( " + " ); + ctx.output.push_str(" + "); } non_empty = true; - write!( &mut ctx.output, "ms({})", d.ms ).unwrap(); + write!(&mut ctx.output, "ms({})", d.ms).unwrap(); } if d.us > 0 { if non_empty { - ctx.output.push_str( " + " ); + ctx.output.push_str(" + "); } - write!( &mut ctx.output, "us({})", d.us ).unwrap(); + write!(&mut ctx.output, "us({})", d.us).unwrap(); } } } -impl ToCode for HashSet< BacktraceId > { - fn to_code_impl( &self, ctx: &mut ToCodeContext ) { - ctx.output.push_str( "[" ); +impl ToCode for HashSet { + fn to_code_impl(&self, ctx: &mut ToCodeContext) { + ctx.output.push_str("["); let mut is_first = true; for item in self { if is_first { is_first = false; } else { - ctx.output.push_str( ", " ); + ctx.output.push_str(", "); } - write!( &mut ctx.output, "{}", item.raw() ).unwrap(); + write!(&mut ctx.output, "{}", item.raw()).unwrap(); } - ctx.output.push_str( "]" ); + ctx.output.push_str("]"); } } -impl ToCode for HashSet< MapId > { - fn to_code_impl( &self, ctx: &mut ToCodeContext ) { - ctx.output.push_str( "[" ); +impl ToCode for HashSet { + fn to_code_impl(&self, ctx: &mut ToCodeContext) { + ctx.output.push_str("["); let mut is_first = true; for item in self { if is_first { is_first = false; } else { - ctx.output.push_str( ", " ); + ctx.output.push_str(", "); } - write!( &mut ctx.output, "{}", item.raw() ).unwrap(); + write!(&mut ctx.output, "{}", item.raw()).unwrap(); } - ctx.output.push_str( "]" ); + ctx.output.push_str("]"); } } @@ -3100,7 +3540,7 @@ macro_rules! out_bool { } impl ToCode for crate::filter::RawBacktraceFilter { - fn to_code_impl( &self, ctx: &mut ToCodeContext ) { + fn to_code_impl(&self, ctx: &mut ToCodeContext) { out! { ctx => self.only_passing_through_function self.only_not_passing_through_function @@ -3115,7 +3555,7 @@ impl ToCode for crate::filter::RawBacktraceFilter { } impl ToCode for crate::filter::RawCommonFilter { - fn to_code_impl( &self, ctx: &mut ToCodeContext ) { + fn to_code_impl(&self, ctx: &mut ToCodeContext) { out! { ctx => self.only_larger_or_equal self.only_larger @@ -3144,11 +3584,11 @@ impl ToCode for crate::filter::RawCommonFilter { } impl ToCode for RawAllocationFilter { - fn to_code_impl( &self, ctx: &mut ToCodeContext ) { - writeln!( &mut ctx.output, "{}", ctx.list_source ).unwrap(); + fn to_code_impl(&self, ctx: &mut ToCodeContext) { + writeln!(&mut ctx.output, "{}", ctx.list_source).unwrap(); - self.backtrace_filter.to_code_impl( ctx ); - self.common_filter.to_code_impl( ctx ); + self.backtrace_filter.to_code_impl(ctx); + self.common_filter.to_code_impl(ctx); out! { ctx => self.only_first_size_larger_or_equal @@ -3193,11 +3633,11 @@ impl ToCode for RawAllocationFilter { } impl ToCode for RawMapFilter { - fn to_code_impl( &self, ctx: &mut ToCodeContext ) { - writeln!( &mut ctx.output, "{}", ctx.list_source ).unwrap(); + fn to_code_impl(&self, ctx: &mut ToCodeContext) { + writeln!(&mut ctx.output, "{}", ctx.list_source).unwrap(); - self.backtrace_filter.to_code_impl( ctx ); - self.common_filter.to_code_impl( ctx ); + self.backtrace_filter.to_code_impl(ctx); + self.common_filter.to_code_impl(ctx); out! { ctx => self.only_peak_rss_at_least diff --git a/cli-core/src/script_virtual.rs b/cli-core/src/script_virtual.rs index 3e433f60..df57ba7c 100644 --- a/cli-core/src/script_virtual.rs +++ b/cli-core/src/script_virtual.rs @@ -1,156 +1,167 @@ -use std::sync::{Arc, Weak}; -use std::collections::BTreeMap; use parking_lot::Mutex; +use std::collections::BTreeMap; +use std::sync::{Arc, Weak}; enum NodeKind { - File( Arc< Vec< u8 > > ), - Directory( BTreeMap< String, Arc< Node > > ) + File(Arc>), + Directory(BTreeMap>), } struct Node { - parent: Weak< Node >, + parent: Weak, name: String, - kind: Mutex< NodeKind > + kind: Mutex, } enum VfsErr { NotDir { path: String }, - IsDirectory { path: String } + IsDirectory { path: String }, } impl std::fmt::Display for VfsErr { - fn fmt( &self, fmt: &mut std::fmt::Formatter ) -> std::fmt::Result { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { match *self { VfsErr::NotDir { ref path } => { - write!( fmt, "is not a directory: \"{}\"", path ) - }, + write!(fmt, "is not a directory: \"{}\"", path) + } VfsErr::IsDirectory { ref path } => { - write!( fmt, "is a directory: \"{}\"", path ) - }, + write!(fmt, "is a directory: \"{}\"", path) + } } } } -impl From< VfsErr > for Box< rhai::EvalAltResult > { - fn from( err: VfsErr ) -> Self { - Box::new( err.to_string().into() ) +impl From for Box { + fn from(err: VfsErr) -> Self { + Box::new(err.to_string().into()) } } impl Node { - pub fn parent( &self ) -> Option< Arc< Node > > { + pub fn parent(&self) -> Option> { self.parent.upgrade() } - pub fn absolute_path_into( &self, output: &mut String ) { - if let Some( parent ) = self.parent() { - parent.absolute_path_into( output ); - if !output.ends_with( '/' ) { - output.push( '/' ); + pub fn absolute_path_into(&self, output: &mut String) { + if let Some(parent) = self.parent() { + parent.absolute_path_into(output); + if !output.ends_with('/') { + output.push('/'); } - output.push_str( &self.name ); + output.push_str(&self.name); } else { - output.push( '/' ); + output.push('/'); } } - pub fn absolute_path( &self ) -> String { + pub fn absolute_path(&self) -> String { let mut output = String::new(); - self.absolute_path_into( &mut output ); + self.absolute_path_into(&mut output); output } - pub fn child_path( &self, name: &str ) -> String { + pub fn child_path(&self, name: &str) -> String { let mut output = String::new(); - self.absolute_path_into( &mut output ); - if !output.ends_with( '/' ) { - output.push( '/' ); + self.absolute_path_into(&mut output); + if !output.ends_with('/') { + output.push('/'); } - output.push_str( name ); + output.push_str(name); output } - pub fn get_child( &self, name: &str ) -> Result< Option< Arc< Node > >, VfsErr > { + pub fn get_child(&self, name: &str) -> Result>, VfsErr> { match *self.kind.lock() { - NodeKind::File( .. ) => Err( VfsErr::NotDir { path: self.absolute_path() } ), - NodeKind::Directory( ref directory ) => Ok( directory.get( name ).cloned() ) + NodeKind::File(..) => Err(VfsErr::NotDir { + path: self.absolute_path(), + }), + NodeKind::Directory(ref directory) => Ok(directory.get(name).cloned()), } } - pub fn get_directory_by_relative_path( self: &Arc< Node >, path: &str ) -> Result< Arc< Node >, VfsErr > { + pub fn get_directory_by_relative_path( + self: &Arc, + path: &str, + ) -> Result, VfsErr> { if path.is_empty() { - return Ok( self.clone() ); + return Ok(self.clone()); } let mut node = self.clone(); - for chunk in path.split( "/" ) { - node = match node.get_child( chunk )? { - Some( node ) => node, + for chunk in path.split("/") { + node = match node.get_child(chunk)? { + Some(node) => node, None => { - return Err( VfsErr::NotDir { path: node.child_path( chunk ) } ); + return Err(VfsErr::NotDir { + path: node.child_path(chunk), + }); } } } if !node.is_dir() { - return Err( VfsErr::NotDir { path: path.into() }.into() ) + return Err(VfsErr::NotDir { path: path.into() }.into()); } - Ok( node ) + Ok(node) } - pub fn is_dir( &self ) -> bool { + pub fn is_dir(&self) -> bool { match *self.kind.lock() { - NodeKind::File( .. ) => false, - NodeKind::Directory( .. ) => true + NodeKind::File(..) => false, + NodeKind::Directory(..) => true, } } - pub fn mkdir( self: &Arc< Node >, name: &str ) -> Result< Arc< Node >, VfsErr > { + pub fn mkdir(self: &Arc, name: &str) -> Result, VfsErr> { match *self.kind.lock() { - NodeKind::File( .. ) => Err( VfsErr::NotDir { path: self.absolute_path() } ), - NodeKind::Directory( ref mut directory ) => { - if let Some( child ) = directory.get( name ) { + NodeKind::File(..) => Err(VfsErr::NotDir { + path: self.absolute_path(), + }), + NodeKind::Directory(ref mut directory) => { + if let Some(child) = directory.get(name) { if !child.is_dir() { - return Err( VfsErr::NotDir { path: self.child_path( name ) } ); + return Err(VfsErr::NotDir { + path: self.child_path(name), + }); } - return Ok( child.clone() ); + return Ok(child.clone()); } - let node = Arc::new( Node { - parent: Arc::downgrade( &self ), + let node = Arc::new(Node { + parent: Arc::downgrade(&self), name: name.to_owned(), - kind: Mutex::new( NodeKind::Directory( Default::default() ) ) + kind: Mutex::new(NodeKind::Directory(Default::default())), }); - directory.insert( name.to_owned(), node.clone() ); - Ok( node ) + directory.insert(name.to_owned(), node.clone()); + Ok(node) } } } - pub fn get_or_create_file( self: &Arc< Node >, filename: &str ) -> Result< Arc< Node >, VfsErr > { + pub fn get_or_create_file(self: &Arc, filename: &str) -> Result, VfsErr> { match *self.kind.lock() { - NodeKind::File( .. ) => Err( VfsErr::NotDir { path: self.absolute_path() } ), - NodeKind::Directory( ref mut directory ) => { - if let Some( child ) = directory.get( filename ) { + NodeKind::File(..) => Err(VfsErr::NotDir { + path: self.absolute_path(), + }), + NodeKind::Directory(ref mut directory) => { + if let Some(child) = directory.get(filename) { match *child.kind.lock() { - NodeKind::Directory( .. ) => { - Err( VfsErr::IsDirectory { path: self.child_path( filename ) } ) - }, - NodeKind::File( .. ) => { - Ok( child.clone() ) - } + NodeKind::Directory(..) => Err(VfsErr::IsDirectory { + path: self.child_path(filename), + }), + NodeKind::File(..) => Ok(child.clone()), } } else { - let node = Arc::new( Node { - parent: Arc::downgrade( &self ), + let node = Arc::new(Node { + parent: Arc::downgrade(&self), name: filename.to_owned(), - kind: Mutex::new( NodeKind::File( Default::default() ) ) + kind: Mutex::new(NodeKind::File(Default::default())), }); - directory.insert( filename.to_owned(), node.clone() ); - Ok( node ) + directory.insert(filename.to_owned(), node.clone()); + Ok(node) } } } @@ -158,72 +169,70 @@ impl Node { } pub enum ScriptOutputKind { - PrintLine( String ), - Image { - path: String, - data: Arc< Vec< u8 > > - } + PrintLine(String), + Image { path: String, data: Arc> }, } pub struct VirtualEnvironment { cwd: String, - root: Arc< Node >, - pub output: Vec< ScriptOutputKind > + root: Arc, + pub output: Vec, } impl VirtualEnvironment { pub fn new() -> Self { VirtualEnvironment { cwd: "/".into(), - root: Arc::new( Node { + root: Arc::new(Node { parent: Weak::new(), name: "".into(), - kind: Mutex::new( NodeKind::Directory( Default::default() ) ) + kind: Mutex::new(NodeKind::Directory(Default::default())), }), - output: Default::default() + output: Default::default(), } } - fn normalize_path( &self, mut path: &str ) -> String { + fn normalize_path(&self, mut path: &str) -> String { if path == "/" { return path.into(); } - if path.ends_with( "/" ) { - path = &path[ ..path.len() - 1 ]; + if path.ends_with("/") { + path = &path[..path.len() - 1]; } - if path.starts_with( "/" ) { + if path.starts_with("/") { return path.into(); } - let mut output = String::with_capacity( self.cwd.len() + path.len() + 1 ); - output.push_str( &self.cwd ); - if !output.ends_with( "/" ) { - output.push( '/' ); + let mut output = String::with_capacity(self.cwd.len() + path.len() + 1); + output.push_str(&self.cwd); + if !output.ends_with("/") { + output.push('/'); } - output.push_str( path ); + output.push_str(path); output } } impl crate::script::Environment for VirtualEnvironment { - fn println( &mut self, message: &str ) { - self.output.push( ScriptOutputKind::PrintLine( message.into() ) ); + fn println(&mut self, message: &str) { + self.output + .push(ScriptOutputKind::PrintLine(message.into())); } - fn mkdir_p( &mut self, path: &str ) -> Result< (), Box< rhai::EvalAltResult > > { - let path = self.normalize_path( path ); + fn mkdir_p(&mut self, path: &str) -> Result<(), Box> { + let path = self.normalize_path(path); let mut node = self.root.clone(); - for chunk in path[ 1.. ].split( "/" ) { - node = node.mkdir( chunk )?; + for chunk in path[1..].split("/") { + node = node.mkdir(chunk)?; } Ok(()) } - fn chdir( &mut self, path: &str ) -> Result< (), Box< rhai::EvalAltResult > > { - let path = self.normalize_path( path ); - self.root.get_directory_by_relative_path( &path[ 1.. ] )?; + fn chdir(&mut self, path: &str) -> Result<(), Box> { + let path = self.normalize_path(path); + self.root.get_directory_by_relative_path(&path[1..])?; self.cwd = path.into(); Ok(()) } @@ -232,38 +241,37 @@ impl crate::script::Environment for VirtualEnvironment { &mut self, path: &str, kind: crate::script::FileKind, - contents: &[u8] - ) -> Result< (), Box< rhai::EvalAltResult > > { - let path = self.normalize_path( path ); - let index = path.rfind( "/" ).unwrap(); - let dirname = &path[ ..std::cmp::max( 1, index ) ]; - let filename = &path[ index + 1.. ]; + contents: &[u8], + ) -> Result<(), Box> { + let path = self.normalize_path(path); + let index = path.rfind("/").unwrap(); + let dirname = &path[..std::cmp::max(1, index)]; + let filename = &path[index + 1..]; if filename.is_empty() { - return Err( crate::script::error( "missing filename" ) ); + return Err(crate::script::error("missing filename")); } - let directory = self.root.get_directory_by_relative_path( &dirname[ 1.. ] )?; - let child = - if let Some( node ) = directory.get_child( filename )? { - if node.is_dir() { - return Err( VfsErr::IsDirectory { path }.into() ) - } + let directory = self.root.get_directory_by_relative_path(&dirname[1..])?; + let child = if let Some(node) = directory.get_child(filename)? { + if node.is_dir() { + return Err(VfsErr::IsDirectory { path }.into()); + } - node - } else { - directory.get_or_create_file( filename )? - }; + node + } else { + directory.get_or_create_file(filename)? + }; - let contents: Vec< u8 > = contents.into(); - let contents = Arc::new( contents ); - *child.kind.lock() = NodeKind::File( contents.clone() ); + let contents: Vec = contents.into(); + let contents = Arc::new(contents); + *child.kind.lock() = NodeKind::File(contents.clone()); use crate::script::FileKind; match kind { FileKind::Svg => { - self.output.push( ScriptOutputKind::Image { + self.output.push(ScriptOutputKind::Image { path, - data: contents + data: contents, }); } } diff --git a/cli-core/src/squeeze.rs b/cli-core/src/squeeze.rs index d8b7d0ad..45c1caff 100644 --- a/cli-core/src/squeeze.rs +++ b/cli-core/src/squeeze.rs @@ -1,19 +1,13 @@ -use std::io::{self, Read, Write}; use std::cmp::{max, min}; +use std::io::{self, Read, Write}; -use std::collections::hash_map::Entry; use ahash::AHashMap as HashMap; +use std::collections::hash_map::Entry; +use common::speedy::Writable; use common::Timestamp; -use common::speedy::{ - Writable -}; -use common::event::{ - Event, - AllocBody, - AllocationId -}; +use common::event::{AllocBody, AllocationId, Event}; use crate::loader::Loader; use crate::threaded_lz4_stream::Lz4Writer; @@ -22,46 +16,59 @@ use crate::reader::parse_events; struct Allocation { counter: u64, - events: smallvec::SmallVec< [BufferedAllocation; 1] > + events: smallvec::SmallVec<[BufferedAllocation; 1]>, } impl Allocation { - pub fn new( counter: u64 ) -> Self { + pub fn new(counter: u64) -> Self { Allocation { counter, - events: Default::default() + events: Default::default(), } } } struct BufferedAllocation { timestamp: Timestamp, - allocation: AllocBody + allocation: AllocBody, } -fn emit( id: AllocationId, mut events: smallvec::SmallVec< [BufferedAllocation; 1] >, fp: &mut impl Write ) -> Result< (), std::io::Error > { +fn emit( + id: AllocationId, + mut events: smallvec::SmallVec<[BufferedAllocation; 1]>, + fp: &mut impl Write, +) -> Result<(), std::io::Error> { if events.len() == 0 { return Ok(()); } - let mut iter = events.drain( .. ); + let mut iter = events.drain(..); - let BufferedAllocation { timestamp, allocation } = iter.next().unwrap(); + let BufferedAllocation { + timestamp, + allocation, + } = iter.next().unwrap(); let mut old_pointer = allocation.pointer; Event::AllocEx { id, timestamp, - allocation - }.write_to_stream( &mut *fp )?; + allocation, + } + .write_to_stream(&mut *fp)?; - while let Some( BufferedAllocation { timestamp, allocation } ) = iter.next() { + while let Some(BufferedAllocation { + timestamp, + allocation, + }) = iter.next() + { let new_pointer = allocation.pointer; Event::ReallocEx { id, timestamp, old_pointer, - allocation - }.write_to_stream( &mut *fp )?; + allocation, + } + .write_to_stream(&mut *fp)?; old_pointer = new_pointer; } @@ -74,28 +81,33 @@ struct GroupStatistics { free_count: u64, free_size: u64, min_size: u64, - max_size: u64 + max_size: u64, } -pub fn squeeze_data< F, G >( input_fp: F, output_fp: G, threshold: Option< u64 > ) -> Result< (), io::Error > - where F: Read + Send + 'static, - G: Write + Send + 'static +pub fn squeeze_data( + input_fp: F, + output_fp: G, + threshold: Option, +) -> Result<(), io::Error> +where + F: Read + Send + 'static, + G: Write + Send + 'static, { - let (header, event_stream) = parse_events( input_fp )?; + let (header, event_stream) = parse_events(input_fp)?; let mut current_timestamp = header.initial_timestamp; - let mut ofp = Lz4Writer::new( output_fp ); - Event::Header( header ).write_to_stream( &mut ofp )?; - let threshold = threshold.map( Timestamp::from_secs ); + let mut ofp = Lz4Writer::new(output_fp); + Event::Header(header).write_to_stream(&mut ofp)?; + let threshold = threshold.map(Timestamp::from_secs); { let mut previous_backtrace_on_thread = HashMap::new(); - let mut backtrace_cache: HashMap< Vec< u64 >, u64 > = Default::default(); - let mut backtrace_map: HashMap< u64, u64 > = Default::default(); - let mut stats_by_backtrace: HashMap< u64, GroupStatistics > = Default::default(); - let mut young_allocations_by_id: HashMap< AllocationId, Allocation > = Default::default(); - let mut mature_allocations_by_id: HashMap< AllocationId, Allocation > = Default::default(); - let mut allocations_by_pointer: HashMap< u64, Allocation > = Default::default(); + let mut backtrace_cache: HashMap, u64> = Default::default(); + let mut backtrace_map: HashMap = Default::default(); + let mut stats_by_backtrace: HashMap = Default::default(); + let mut young_allocations_by_id: HashMap = Default::default(); + let mut mature_allocations_by_id: HashMap = Default::default(); + let mut allocations_by_pointer: HashMap = Default::default(); let mut last_flush = current_timestamp; let mut flushed_buffer = Vec::new(); let mut allocation_counter = 0; @@ -103,326 +115,424 @@ pub fn squeeze_data< F, G >( input_fp: F, output_fp: G, threshold: Option< u64 > for event in event_stream { let event = event?; let mut event = match event { - Event::Alloc { timestamp, allocation } => { - Event::AllocEx { - id: AllocationId::UNTRACKED, - timestamp, - allocation - } + Event::Alloc { + timestamp, + allocation, + } => Event::AllocEx { + id: AllocationId::UNTRACKED, + timestamp, + allocation, }, - Event::Realloc { timestamp, old_pointer, allocation } => { - Event::ReallocEx { - id: AllocationId::UNTRACKED, - timestamp, - old_pointer, - allocation - } + Event::Realloc { + timestamp, + old_pointer, + allocation, + } => Event::ReallocEx { + id: AllocationId::UNTRACKED, + timestamp, + old_pointer, + allocation, }, - Event::Free { timestamp, pointer, backtrace, thread } => { - Event::FreeEx { - id: AllocationId::UNTRACKED, - timestamp, - pointer, - backtrace, - thread - } + Event::Free { + timestamp, + pointer, + backtrace, + thread, + } => Event::FreeEx { + id: AllocationId::UNTRACKED, + timestamp, + pointer, + backtrace, + thread, }, - event => event + event => event, }; - if let Some( threshold ) = threshold { + if let Some(threshold) = threshold { if current_timestamp - last_flush > threshold { - std::mem::swap( &mut young_allocations_by_id, &mut mature_allocations_by_id ); + std::mem::swap(&mut young_allocations_by_id, &mut mature_allocations_by_id); let mut allocations_kept = 0; for (id, allocation) in young_allocations_by_id.drain() { - if allocation.events[0].timestamp < current_timestamp && current_timestamp - allocation.events[0].timestamp >= threshold { - flushed_buffer.push( (id, allocation) ); + if allocation.events[0].timestamp < current_timestamp + && current_timestamp - allocation.events[0].timestamp >= threshold + { + flushed_buffer.push((id, allocation)); } else { // This should not happen, but let's handle it just in case. - mature_allocations_by_id.insert( id, allocation ); + mature_allocations_by_id.insert(id, allocation); allocations_kept += 1; } } if allocations_kept > 0 { - info!( "Was unable to flush {} allocation(s)", allocations_kept ); + info!("Was unable to flush {} allocation(s)", allocations_kept); } last_flush = current_timestamp; // Sort it so that the output doesn't differ based on the hashmap's iteration order. - flushed_buffer.sort_unstable_by_key( |(_, allocation)| allocation.counter ); + flushed_buffer.sort_unstable_by_key(|(_, allocation)| allocation.counter); - for (id, allocation) in flushed_buffer.drain( .. ) { - emit( id, allocation.events, &mut ofp )?; + for (id, allocation) in flushed_buffer.drain(..) { + emit(id, allocation.events, &mut ofp)?; } } } match event { - | Event::Alloc { .. } - | Event::Realloc { .. } - | Event::Free { .. } - => unreachable!(), + Event::Alloc { .. } | Event::Realloc { .. } | Event::Free { .. } => unreachable!(), Event::Backtrace { id, ref addresses } => { let addresses = addresses.clone().into_owned(); - let new_id = backtrace_cache.entry( addresses ).or_insert( id ); - backtrace_map.insert( id, *new_id ); + let new_id = backtrace_cache.entry(addresses).or_insert(id); + backtrace_map.insert(id, *new_id); if id != *new_id { continue; } - }, + } Event::Backtrace32 { id, ref addresses } => { - let addresses = addresses.iter().map( |&p| p as u64 ).collect(); - let new_id = backtrace_cache.entry( addresses ).or_insert( id ); - backtrace_map.insert( id, *new_id ); + let addresses = addresses.iter().map(|&p| p as u64).collect(); + let new_id = backtrace_cache.entry(addresses).or_insert(id); + backtrace_map.insert(id, *new_id); if id != *new_id { continue; } - }, - Event::PartialBacktrace { id, thread, frames_invalidated, ref mut addresses } => { - let addresses = Loader::expand_partial_backtrace( &mut previous_backtrace_on_thread, thread, frames_invalidated, addresses.iter().cloned() ); - *previous_backtrace_on_thread.get_mut( &thread ).unwrap() = addresses.clone(); - - let new_id = backtrace_cache.entry( addresses.clone() ).or_insert( id ); - backtrace_map.insert( id, *new_id ); + } + Event::PartialBacktrace { + id, + thread, + frames_invalidated, + ref mut addresses, + } => { + let addresses = Loader::expand_partial_backtrace( + &mut previous_backtrace_on_thread, + thread, + frames_invalidated, + addresses.iter().cloned(), + ); + *previous_backtrace_on_thread.get_mut(&thread).unwrap() = addresses.clone(); + + let new_id = backtrace_cache.entry(addresses.clone()).or_insert(id); + backtrace_map.insert(id, *new_id); if id != *new_id { continue; } - let event = Event::Backtrace { id, addresses: addresses.into() }; - event.write_to_stream( &mut ofp )?; + let event = Event::Backtrace { + id, + addresses: addresses.into(), + }; + event.write_to_stream(&mut ofp)?; continue; - }, - Event::PartialBacktrace32 { id, thread, frames_invalidated, ref mut addresses } => { - let addresses = Loader::expand_partial_backtrace( &mut previous_backtrace_on_thread, thread, frames_invalidated, addresses.iter().map( |&address| address as u64 ) ); - *previous_backtrace_on_thread.get_mut( &thread ).unwrap() = addresses.clone(); - - let new_id = backtrace_cache.entry( addresses.clone() ).or_insert( id ); - backtrace_map.insert( id, *new_id ); + } + Event::PartialBacktrace32 { + id, + thread, + frames_invalidated, + ref mut addresses, + } => { + let addresses = Loader::expand_partial_backtrace( + &mut previous_backtrace_on_thread, + thread, + frames_invalidated, + addresses.iter().map(|&address| address as u64), + ); + *previous_backtrace_on_thread.get_mut(&thread).unwrap() = addresses.clone(); + + let new_id = backtrace_cache.entry(addresses.clone()).or_insert(id); + backtrace_map.insert(id, *new_id); if id != *new_id { continue; } - let event = Event::Backtrace { id, addresses: addresses.into() }; - event.write_to_stream( &mut ofp )?; + let event = Event::Backtrace { + id, + addresses: addresses.into(), + }; + event.write_to_stream(&mut ofp)?; continue; - }, - Event::AllocEx { mut allocation, timestamp, id, .. } => { - current_timestamp = std::cmp::max( timestamp, current_timestamp ); + } + Event::AllocEx { + mut allocation, + timestamp, + id, + .. + } => { + current_timestamp = std::cmp::max(timestamp, current_timestamp); let usable_size = allocation.size + allocation.extra_usable_space as u64; { - allocation.backtrace = backtrace_map.get( &allocation.backtrace ).copied().unwrap(); - let stats = stats_by_backtrace.entry( allocation.backtrace ).or_insert_with( || { - GroupStatistics { + allocation.backtrace = + backtrace_map.get(&allocation.backtrace).copied().unwrap(); + let stats = stats_by_backtrace + .entry(allocation.backtrace) + .or_insert_with(|| GroupStatistics { first_allocation: timestamp, last_allocation: timestamp, free_count: 0, free_size: 0, min_size: usable_size, - max_size: usable_size - } - }); + max_size: usable_size, + }); - stats.first_allocation = min( stats.first_allocation, timestamp ); - stats.last_allocation = max( stats.last_allocation, timestamp ); - stats.min_size = min( stats.min_size, usable_size ); - stats.max_size = min( stats.max_size, usable_size ); + stats.first_allocation = min(stats.first_allocation, timestamp); + stats.last_allocation = max(stats.last_allocation, timestamp); + stats.min_size = min(stats.min_size, usable_size); + stats.max_size = min(stats.max_size, usable_size); } let entry; if !id.is_invalid() && !id.is_untracked() { - entry = match young_allocations_by_id.entry( id ) { - Entry::Vacant( entry ) => { + entry = match young_allocations_by_id.entry(id) { + Entry::Vacant(entry) => { let counter = allocation_counter; allocation_counter += 1; - let allocation = Allocation::new( counter ); - entry.insert( allocation ) - }, - Entry::Occupied( .. ) => { - warn!( "Duplicate allocation with ID: {:?}", id ); + let allocation = Allocation::new(counter); + entry.insert(allocation) + } + Entry::Occupied(..) => { + warn!("Duplicate allocation with ID: {:?}", id); continue; } }; } else { - entry = match allocations_by_pointer.entry( allocation.pointer ) { - Entry::Vacant( entry ) => { + entry = match allocations_by_pointer.entry(allocation.pointer) { + Entry::Vacant(entry) => { let counter = allocation_counter; allocation_counter += 1; - let allocation = Allocation::new( counter ); - entry.insert( allocation ) - }, - Entry::Occupied( .. ) => { - warn!( "Duplicate allocation with address: 0x{:016X}", allocation.pointer ); + let allocation = Allocation::new(counter); + entry.insert(allocation) + } + Entry::Occupied(..) => { + warn!( + "Duplicate allocation with address: 0x{:016X}", + allocation.pointer + ); continue; } }; } - entry.events.push( BufferedAllocation { timestamp, allocation } ); + entry.events.push(BufferedAllocation { + timestamp, + allocation, + }); continue; - }, - Event::ReallocEx { timestamp, mut allocation, old_pointer, id, .. } => { + } + Event::ReallocEx { + timestamp, + mut allocation, + old_pointer, + id, + .. + } => { let usable_size = allocation.size + allocation.extra_usable_space as u64; { - allocation.backtrace = backtrace_map.get( &allocation.backtrace ).copied().unwrap(); - let stats = stats_by_backtrace.entry( allocation.backtrace ).or_insert_with( || { - GroupStatistics { + allocation.backtrace = + backtrace_map.get(&allocation.backtrace).copied().unwrap(); + let stats = stats_by_backtrace + .entry(allocation.backtrace) + .or_insert_with(|| GroupStatistics { first_allocation: timestamp, last_allocation: timestamp, free_count: 0, free_size: 0, min_size: usable_size, - max_size: usable_size - } - }); + max_size: usable_size, + }); - stats.first_allocation = min( stats.first_allocation, timestamp ); - stats.last_allocation = max( stats.last_allocation, timestamp ); - stats.min_size = min( stats.min_size, usable_size ); - stats.max_size = min( stats.max_size, usable_size ); + stats.first_allocation = min(stats.first_allocation, timestamp); + stats.last_allocation = max(stats.last_allocation, timestamp); + stats.min_size = min(stats.min_size, usable_size); + stats.max_size = min(stats.max_size, usable_size); } let entry; if !id.is_invalid() && !id.is_untracked() { - entry = match young_allocations_by_id.get_mut( &id ) { - Some( entry ) => entry, - None => { - match mature_allocations_by_id.get_mut( &id ) { - Some( entry ) => entry, - None => { - let event = Event::ReallocEx { timestamp, allocation, old_pointer, id }; - event.write_to_stream( &mut ofp )?; - continue; - } + entry = match young_allocations_by_id.get_mut(&id) { + Some(entry) => entry, + None => match mature_allocations_by_id.get_mut(&id) { + Some(entry) => entry, + None => { + let event = Event::ReallocEx { + timestamp, + allocation, + old_pointer, + id, + }; + event.write_to_stream(&mut ofp)?; + continue; } - } + }, }; } else { - let old_entry = match allocations_by_pointer.remove( &old_pointer ) { - Some( entry ) => entry, + let old_entry = match allocations_by_pointer.remove(&old_pointer) { + Some(entry) => entry, None => { - warn!( "Invalid reallocation of address: 0x{:016X}", old_pointer ); + warn!("Invalid reallocation of address: 0x{:016X}", old_pointer); continue; } }; - entry = match allocations_by_pointer.entry( allocation.pointer ) { - Entry::Vacant( entry ) => entry.insert( old_entry ), - Entry::Occupied( .. ) => { - warn!( "Duplicate reallocation with address: 0x{:016X}", allocation.pointer ); + entry = match allocations_by_pointer.entry(allocation.pointer) { + Entry::Vacant(entry) => entry.insert(old_entry), + Entry::Occupied(..) => { + warn!( + "Duplicate reallocation with address: 0x{:016X}", + allocation.pointer + ); continue; } }; } - entry.events.push( BufferedAllocation { timestamp, allocation } ); + entry.events.push(BufferedAllocation { + timestamp, + allocation, + }); continue; - }, - Event::FreeEx { id, timestamp, pointer, backtrace, thread } => { + } + Event::FreeEx { + id, + timestamp, + pointer, + backtrace, + thread, + } => { let mut entry; if !id.is_invalid() && !id.is_untracked() { - entry = young_allocations_by_id.remove( &id ); + entry = young_allocations_by_id.remove(&id); if entry.is_none() { - entry = mature_allocations_by_id.remove( &id ); + entry = mature_allocations_by_id.remove(&id); } if entry.is_none() { - let event = Event::FreeEx { id, timestamp, pointer, backtrace, thread }; - event.write_to_stream( &mut ofp )?; + let event = Event::FreeEx { + id, + timestamp, + pointer, + backtrace, + thread, + }; + event.write_to_stream(&mut ofp)?; continue; } } else { - entry = allocations_by_pointer.remove( &pointer ); + entry = allocations_by_pointer.remove(&pointer); } - if let Some( entry ) = entry { + if let Some(entry) = entry { if timestamp < entry.events[0].timestamp { - warn!( "Deallocation in the past of address: 0x{:016X}", pointer ); + warn!("Deallocation in the past of address: 0x{:016X}", pointer); } else { - if let Some( threshold ) = threshold { + if let Some(threshold) = threshold { let lifetime = timestamp - entry.events[0].timestamp; if lifetime > threshold { - emit( id, entry.events, &mut ofp )?; - let event = Event::FreeEx { id, timestamp, pointer, backtrace, thread }; - event.write_to_stream( &mut ofp )?; + emit(id, entry.events, &mut ofp)?; + let event = Event::FreeEx { + id, + timestamp, + pointer, + backtrace, + thread, + }; + event.write_to_stream(&mut ofp)?; continue; } } } for buffered in entry.events { - let usable_size = buffered.allocation.size + buffered.allocation.extra_usable_space as u64; - let stats = stats_by_backtrace.get_mut( &buffered.allocation.backtrace ).unwrap(); + let usable_size = buffered.allocation.size + + buffered.allocation.extra_usable_space as u64; + let stats = stats_by_backtrace + .get_mut(&buffered.allocation.backtrace) + .unwrap(); stats.free_count += 1; stats.free_size += usable_size; } } continue; - }, - Event::MemoryMap { ref mut backtrace, .. } | - Event::MemoryUnmap { ref mut backtrace, .. } | - Event::Mallopt { ref mut backtrace, .. } => { - *backtrace = backtrace_map.get( backtrace ).copied().unwrap(); - }, + } + Event::MemoryMap { + ref mut backtrace, .. + } + | Event::MemoryUnmap { + ref mut backtrace, .. + } + | Event::Mallopt { + ref mut backtrace, .. + } => { + *backtrace = backtrace_map.get(backtrace).copied().unwrap(); + } - Event::GroupStatistics { ref mut backtrace, first_allocation, last_allocation, free_count, free_size, min_size, max_size } => { + Event::GroupStatistics { + ref mut backtrace, + first_allocation, + last_allocation, + free_count, + free_size, + min_size, + max_size, + } => { { - *backtrace = backtrace_map.get( backtrace ).copied().unwrap(); - let stats = stats_by_backtrace.entry( *backtrace ).or_insert_with( || { + *backtrace = backtrace_map.get(backtrace).copied().unwrap(); + let stats = stats_by_backtrace.entry(*backtrace).or_insert_with(|| { GroupStatistics { first_allocation, last_allocation, free_count: 0, free_size: 0, min_size, - max_size + max_size, } }); - stats.first_allocation = min( stats.first_allocation, first_allocation ); - stats.last_allocation = max( stats.last_allocation, last_allocation ); - stats.min_size = min( stats.min_size, min_size ); - stats.max_size = max( stats.max_size, max_size ); + stats.first_allocation = min(stats.first_allocation, first_allocation); + stats.last_allocation = max(stats.last_allocation, last_allocation); + stats.min_size = min(stats.min_size, min_size); + stats.max_size = max(stats.max_size, max_size); stats.free_count += free_count; stats.free_size += free_size; } continue; - }, + } - Event::File { .. } => {}, - Event::File64 { .. } => {}, - Event::Header { .. } => {}, - Event::MemoryDump { .. } => {}, - Event::Marker { .. } => {}, - Event::Environ { .. } => {}, - Event::WallClock { .. } => {}, - Event::String { .. } => {}, - Event::DecodedFrame { .. } => {}, - Event::DecodedBacktrace { .. } => {}, - Event::MemoryMapEx { .. } => {}, - Event::AddRegion { .. } => {}, - Event::RemoveRegion { .. } => {}, - Event::UpdateRegionUsage { .. } => {}, + Event::File { .. } => {} + Event::File64 { .. } => {} + Event::Header { .. } => {} + Event::MemoryDump { .. } => {} + Event::Marker { .. } => {} + Event::Environ { .. } => {} + Event::WallClock { .. } => {} + Event::String { .. } => {} + Event::DecodedFrame { .. } => {} + Event::DecodedBacktrace { .. } => {} + Event::MemoryMapEx { .. } => {} + Event::AddRegion { .. } => {} + Event::RemoveRegion { .. } => {} + Event::UpdateRegionUsage { .. } => {} } - event.write_to_stream( &mut ofp )?; + event.write_to_stream(&mut ofp)?; } for (id, bucket) in mature_allocations_by_id { - emit( id, bucket.events, &mut ofp )?; + emit(id, bucket.events, &mut ofp)?; } for (id, bucket) in young_allocations_by_id { - emit( id, bucket.events, &mut ofp )?; + emit(id, bucket.events, &mut ofp)?; } for (_, bucket) in allocations_by_pointer { - emit( common::event::AllocationId::UNTRACKED, bucket.events, &mut ofp )?; + emit( + common::event::AllocationId::UNTRACKED, + bucket.events, + &mut ofp, + )?; } for (backtrace, stats) in stats_by_backtrace { @@ -433,9 +543,9 @@ pub fn squeeze_data< F, G >( input_fp: F, output_fp: G, threshold: Option< u64 > free_count: stats.free_count, free_size: stats.free_size, min_size: stats.min_size, - max_size: stats.max_size + max_size: stats.max_size, }; - event.write_to_stream( &mut ofp )?; + event.write_to_stream(&mut ofp)?; } } diff --git a/cli-core/src/threaded_lz4_stream.rs b/cli-core/src/threaded_lz4_stream.rs index 178f9de5..08141adc 100644 --- a/cli-core/src/threaded_lz4_stream.rs +++ b/cli-core/src/threaded_lz4_stream.rs @@ -1,70 +1,70 @@ +use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; +use lz4_compress; +use parking_lot::Mutex; use std::cmp::min; use std::io::{self, Write}; -use std::thread; use std::marker::PhantomData; use std::mem; use std::sync::Arc; -use lz4_compress; -use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian}; -use parking_lot::Mutex; +use std::thread; -pub struct Lz4Reader< F: io::Read + Send > { - phantom: PhantomData< F >, - output_rx: crossbeam_channel::Receiver< (u64, Vec< u8 >) >, - queue: Vec< (u64, Vec< u8 >) >, +pub struct Lz4Reader { + phantom: PhantomData, + output_rx: crossbeam_channel::Receiver<(u64, Vec)>, + queue: Vec<(u64, Vec)>, counter: u64, - buffer: Vec< u8 >, + buffer: Vec, position: usize, - error: Arc< Mutex< Option< io::Error > > > + error: Arc>>, } -fn read_chunk( fp: &mut impl io::Read, buffer: &mut Vec< u8 > ) -> Result< (Vec< u8 >, bool), io::Error > { +fn read_chunk(fp: &mut impl io::Read, buffer: &mut Vec) -> Result<(Vec, bool), io::Error> { let kind = fp.read_u8()?; if kind != 1 && kind != 2 { unimplemented!(); } - let length = fp.read_u32::< LittleEndian >()? as usize; - buffer.reserve( length ); + let length = fp.read_u32::()? as usize; + buffer.reserve(length); unsafe { - buffer.set_len( length ); + buffer.set_len(length); } - fp.read_exact( buffer )?; - let chunk = mem::replace( buffer, Vec::new() ); - Ok( (chunk, kind == 1) ) + fp.read_exact(buffer)?; + let chunk = mem::replace(buffer, Vec::new()); + Ok((chunk, kind == 1)) } -impl< F: io::Read + Send + 'static > Lz4Reader< F > { - pub fn new( mut fp: F ) -> Self { +impl Lz4Reader { + pub fn new(mut fp: F) -> Self { let thread_count = 1; - let (decompress_tx, decompress_rx) = crossbeam_channel::bounded( 4 ); - let (output_tx, output_rx) = crossbeam_channel::bounded( 4 ); - let error_arc = Arc::new( Mutex::new( None ) ); + let (decompress_tx, decompress_rx) = crossbeam_channel::bounded(4); + let (output_tx, output_rx) = crossbeam_channel::bounded(4); + let error_arc = Arc::new(Mutex::new(None)); let error_arc_clone = error_arc.clone(); let output_tx_clone = output_tx.clone(); - thread::spawn( move || { + thread::spawn(move || { let mut buffer = Vec::new(); let mut counter = 0; loop { - let (chunk, is_compressed) = match read_chunk( &mut fp, &mut buffer ) { - Ok( chunk ) => chunk, - Err( ref error ) if error.kind() == io::ErrorKind::UnexpectedEof => { + let (chunk, is_compressed) = match read_chunk(&mut fp, &mut buffer) { + Ok(chunk) => chunk, + Err(ref error) if error.kind() == io::ErrorKind::UnexpectedEof => { break; - }, - Err( error ) => { - *error_arc_clone.lock() = Some( error ); + } + Err(error) => { + *error_arc_clone.lock() = Some(error); break; } }; if is_compressed { - if decompress_tx.send( (counter, chunk) ).is_err() { + if decompress_tx.send((counter, chunk)).is_err() { break; } } else { - if output_tx_clone.send( (counter, chunk) ).is_err() { + if output_tx_clone.send((counter, chunk)).is_err() { break; } } @@ -76,12 +76,12 @@ impl< F: io::Read + Send + 'static > Lz4Reader< F > { for _ in 0..thread_count { let decompress_rx = decompress_rx.clone(); let output_tx = output_tx.clone(); - thread::spawn( move || { + thread::spawn(move || { let mut output = Vec::new(); - while let Ok( (counter, input) ) = decompress_rx.recv() { + while let Ok((counter, input)) = decompress_rx.recv() { output.clear(); - if let Ok(()) = lz4_compress::decompress_into( &input, &mut output ) { - if output_tx.send( (counter, output.clone()) ).is_err() { + if let Ok(()) = lz4_compress::decompress_into(&input, &mut output) { + if output_tx.send((counter, output.clone())).is_err() { break; } } @@ -96,30 +96,33 @@ impl< F: io::Read + Send + 'static > Lz4Reader< F > { counter: 0, buffer: Vec::new(), position: 0, - error: error_arc + error: error_arc, } } } -impl< F: io::Read + Send > Lz4Reader< F > { +impl Lz4Reader { #[inline(always)] - fn read_cached( &mut self, buf: &mut [u8] ) -> usize { - let len = min( buf.len(), self.buffer.len() - self.position ); - buf[ ..len ].copy_from_slice( &self.buffer[ self.position..self.position + len ] ); + fn read_cached(&mut self, buf: &mut [u8]) -> usize { + let len = min(buf.len(), self.buffer.len() - self.position); + buf[..len].copy_from_slice(&self.buffer[self.position..self.position + len]); self.position += len; len } #[inline(never)] - fn read_slow( &mut self, buf: &mut [u8] ) -> io::Result< usize > { + fn read_slow(&mut self, buf: &mut [u8]) -> io::Result { 'outer: loop { if self.buffer.len() - self.position > 0 { - return Ok( self.read_cached( buf ) ); + return Ok(self.read_cached(buf)); } - let index = self.queue.iter().position( |(counter, _)| *counter == self.counter ); - if let Some( index ) = index { - let (_, buffer) = self.queue.swap_remove( index ); + let index = self + .queue + .iter() + .position(|(counter, _)| *counter == self.counter); + if let Some(index) = index { + let (_, buffer) = self.queue.swap_remove(index); self.buffer = buffer; self.position = 0; self.counter += 1; @@ -128,13 +131,13 @@ impl< F: io::Read + Send > Lz4Reader< F > { loop { let (counter, buffer) = match self.output_rx.recv() { - Ok( (counter, buffer) ) => (counter, buffer), - Err( .. ) => { - if let Some( error ) = self.error.lock().take() { - return Err( error ); + Ok((counter, buffer)) => (counter, buffer), + Err(..) => { + if let Some(error) = self.error.lock().take() { + return Err(error); } - return Ok( 0 ); + return Ok(0); } }; @@ -144,126 +147,131 @@ impl< F: io::Read + Send > Lz4Reader< F > { self.counter += 1; continue 'outer; } else { - self.queue.push( (counter, buffer) ); + self.queue.push((counter, buffer)); } } } } #[inline(never)] - fn read_exact_slow( &mut self, mut buf: &mut [u8] ) -> io::Result< () > { + fn read_exact_slow(&mut self, mut buf: &mut [u8]) -> io::Result<()> { while !buf.is_empty() { - match io::Read::read( self, buf ) { - Ok( 0 ) => break, - Ok( n ) => { + match io::Read::read(self, buf) { + Ok(0) => break, + Ok(n) => { let tmp = buf; buf = &mut tmp[n..]; } - Err( ref e ) if e.kind() == io::ErrorKind::Interrupted => {} - Err( e ) => return Err( e ) + Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} + Err(e) => return Err(e), } } if !buf.is_empty() { - Err( io::Error::new( io::ErrorKind::UnexpectedEof, "failed to fill whole buffer" ) ) + Err(io::Error::new( + io::ErrorKind::UnexpectedEof, + "failed to fill whole buffer", + )) } else { Ok(()) } } } -impl< F: io::Read + Send > io::Read for Lz4Reader< F > { +impl io::Read for Lz4Reader { #[inline(always)] - fn read( &mut self, buf: &mut [u8] ) -> io::Result< usize > { + fn read(&mut self, buf: &mut [u8]) -> io::Result { if self.buffer.len() - self.position > 0 { - return Ok( self.read_cached( buf ) ); + return Ok(self.read_cached(buf)); } - self.read_slow( buf ) + self.read_slow(buf) } #[inline(always)] - fn read_exact( &mut self, buf: &mut [u8] ) -> io::Result< () > { + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { if self.buffer.len() - self.position >= buf.len() { - self.read_cached( buf ); + self.read_cached(buf); return Ok(()); } - self.read_exact_slow( buf ) + self.read_exact_slow(buf) } } -pub struct Lz4Writer< F: io::Write + Send + 'static > { - phantom: PhantomData< F >, - compress_tx: Option< crossbeam_channel::Sender< (u64, Vec< u8 >) > >, - thread_handle: Option< thread::JoinHandle< Result< (), io::Error > > >, - buffer: Vec< u8 >, - counter: u64 +pub struct Lz4Writer { + phantom: PhantomData, + compress_tx: Option)>>, + thread_handle: Option>>, + buffer: Vec, + counter: u64, } -impl< F: io::Write + Send + 'static > Lz4Writer< F > { - pub fn new( mut fp: F ) -> Self { +impl Lz4Writer { + pub fn new(mut fp: F) -> Self { let thread_count = 1; - let (compress_tx, compress_rx) = crossbeam_channel::bounded( 4 ); - let (merge_tx, merge_rx) = crossbeam_channel::bounded( 4 ); + let (compress_tx, compress_rx) = crossbeam_channel::bounded(4); + let (merge_tx, merge_rx) = crossbeam_channel::bounded(4); for _ in 0..thread_count { let compress_rx = compress_rx.clone(); let merge_tx = merge_tx.clone(); - thread::spawn( move || { - while let Ok( (counter, chunk) ) = compress_rx.recv() { - let chunk: Vec< u8 > = chunk; - let buffer = lz4_compress::compress( &chunk ); - if merge_tx.send( (counter, buffer) ).is_err() { + thread::spawn(move || { + while let Ok((counter, chunk)) = compress_rx.recv() { + let chunk: Vec = chunk; + let buffer = lz4_compress::compress(&chunk); + if merge_tx.send((counter, buffer)).is_err() { break; } } }); } - let thread_handle = thread::spawn( move || { + let thread_handle = thread::spawn(move || { let mut expected_counter = 0; let mut queue = Vec::new(); - 'outer: while let Ok( (counter, mut buffer) ) = merge_rx.recv() { + 'outer: while let Ok((counter, mut buffer)) = merge_rx.recv() { if counter != expected_counter { - queue.push( (counter, buffer) ); + queue.push((counter, buffer)); continue; } loop { - fp.write_u8( 1 )?; - fp.write_u32::< LittleEndian >( buffer.len() as u32 )?; - fp.write_all( &buffer )?; + fp.write_u8(1)?; + fp.write_u32::(buffer.len() as u32)?; + fp.write_all(&buffer)?; expected_counter += 1; - let index = queue.iter().position( |(counter, _)| *counter == expected_counter ); - if let Some( index ) = index { - buffer = queue.swap_remove( index ).1; + let index = queue + .iter() + .position(|(counter, _)| *counter == expected_counter); + if let Some(index) = index { + buffer = queue.swap_remove(index).1; } else { continue 'outer; } } } - let result: Result< (), io::Error > = Ok(()); + let result: Result<(), io::Error> = Ok(()); result }); Lz4Writer { phantom: PhantomData, - compress_tx: Some( compress_tx ), - thread_handle: Some( thread_handle ), + compress_tx: Some(compress_tx), + thread_handle: Some(thread_handle), buffer: Vec::new(), - counter: 0 + counter: 0, } } - fn join( &mut self ) -> Result< (), io::Error > { + fn join(&mut self) -> Result<(), io::Error> { self.flush()?; self.compress_tx = None; - if let Some( handle ) = self.thread_handle.take() { + if let Some(handle) = self.thread_handle.take() { handle.join().unwrap() } else { Ok(()) @@ -271,40 +279,46 @@ impl< F: io::Write + Send + 'static > Lz4Writer< F > { } } -impl< F: io::Write + Send + 'static > Drop for Lz4Writer< F > { - fn drop( &mut self ) { +impl Drop for Lz4Writer { + fn drop(&mut self) { let _ = self.join(); } } -impl< F: io::Write + Send + 'static > io::Write for Lz4Writer< F > { +impl io::Write for Lz4Writer { #[inline(always)] - fn write( &mut self, slice: &[u8] ) -> io::Result< usize > { - self.buffer.extend_from_slice( slice ); + fn write(&mut self, slice: &[u8]) -> io::Result { + self.buffer.extend_from_slice(slice); if self.buffer.len() >= 512 * 1024 { self.flush()?; } - Ok( slice.len() ) + Ok(slice.len()) } #[inline(always)] - fn write_all( &mut self, buf: &[u8] ) -> Result< (), io::Error > { - self.write( buf )?; + fn write_all(&mut self, buf: &[u8]) -> Result<(), io::Error> { + self.write(buf)?; Ok(()) } #[inline(never)] - fn flush( &mut self ) -> io::Result< () > { + fn flush(&mut self) -> io::Result<()> { if self.buffer.is_empty() { return Ok(()); } let next_length = self.buffer.len() + 1024 * 8; - let buffer = mem::replace( &mut self.buffer, Vec::with_capacity( next_length ) ); - if self.compress_tx.as_ref().unwrap().send( (self.counter, buffer) ).is_err() { + let buffer = mem::replace(&mut self.buffer, Vec::with_capacity(next_length)); + if self + .compress_tx + .as_ref() + .unwrap() + .send((self.counter, buffer)) + .is_err() + { self.compress_tx = None; - if let Some( handle ) = self.thread_handle.take() { + if let Some(handle) = self.thread_handle.take() { handle.join().unwrap()?; } } diff --git a/cli-core/src/timeline.rs b/cli-core/src/timeline.rs index 87ce49da..a9c8446a 100644 --- a/cli-core/src/timeline.rs +++ b/cli-core/src/timeline.rs @@ -1,5 +1,5 @@ -use crate::{Data, OperationId, Map, MapUsage, UsageDelta}; use crate::Timestamp; +use crate::{Data, Map, MapUsage, OperationId, UsageDelta}; #[derive(Copy, Clone, derive_more::Add, derive_more::Sub, Default, Debug)] pub struct AllocationDelta { @@ -7,8 +7,8 @@ pub struct AllocationDelta { pub allocations: i64, } -impl< 'a > From< &'a MapUsage > for UsageDelta { - fn from( usage: &'a MapUsage ) -> Self { +impl<'a> From<&'a MapUsage> for UsageDelta { + fn from(usage: &'a MapUsage) -> Self { UsageDelta { address_space: usage.address_space as i64, anonymous: usage.anonymous as i64, @@ -16,82 +16,84 @@ impl< 'a > From< &'a MapUsage > for UsageDelta { shared_dirty: usage.shared_dirty as i64, private_clean: usage.private_clean as i64, private_dirty: usage.private_dirty as i64, - swap: usage.swap as i64 + swap: usage.swap as i64, } } } impl Map { - pub fn emit_ops( &self, output: &mut Vec< (Timestamp, UsageDelta) > ) { + pub fn emit_ops(&self, output: &mut Vec<(Timestamp, UsageDelta)>) { let mut last = UsageDelta::default(); for usage in &self.usage_history { let current: UsageDelta = usage.into(); - output.push( (usage.timestamp, current - last) ); + output.push((usage.timestamp, current - last)); last = current; } } } -pub trait Delta: Copy + Clone + Default + std::ops::Add< Output = Self > + std::ops::Sub< Output = Self > { - fn max( lhs: Self, rhs: Self ) -> Self; - fn min( lhs: Self, rhs: Self ) -> Self; +pub trait Delta: + Copy + Clone + Default + std::ops::Add + std::ops::Sub +{ + fn max(lhs: Self, rhs: Self) -> Self; + fn min(lhs: Self, rhs: Self) -> Self; - fn at_least_zero( self ) -> Self { - Self::max( self, Self::default() ) + fn at_least_zero(self) -> Self { + Self::max(self, Self::default()) } - fn at_most_zero( self ) -> Self { - Self::min( self, Self::default() ) + fn at_most_zero(self) -> Self { + Self::min(self, Self::default()) } } impl Delta for i64 { - fn max( lhs: Self, rhs: Self ) -> Self { - std::cmp::max( lhs, rhs ) + fn max(lhs: Self, rhs: Self) -> Self { + std::cmp::max(lhs, rhs) } - fn min( lhs: Self, rhs: Self ) -> Self { - std::cmp::min( lhs, rhs ) + fn min(lhs: Self, rhs: Self) -> Self { + std::cmp::min(lhs, rhs) } } impl Delta for AllocationDelta { - fn max( lhs: Self, rhs: Self ) -> Self { + fn max(lhs: Self, rhs: Self) -> Self { AllocationDelta { - memory_usage: std::cmp::max( lhs.memory_usage, rhs.memory_usage ), - allocations: std::cmp::max( lhs.allocations, rhs.allocations ) + memory_usage: std::cmp::max(lhs.memory_usage, rhs.memory_usage), + allocations: std::cmp::max(lhs.allocations, rhs.allocations), } } - fn min( lhs: Self, rhs: Self ) -> Self { + fn min(lhs: Self, rhs: Self) -> Self { AllocationDelta { - memory_usage: std::cmp::min( lhs.memory_usage, rhs.memory_usage ), - allocations: std::cmp::min( lhs.allocations, rhs.allocations ) + memory_usage: std::cmp::min(lhs.memory_usage, rhs.memory_usage), + allocations: std::cmp::min(lhs.allocations, rhs.allocations), } } } impl Delta for UsageDelta { - fn max( lhs: Self, rhs: Self ) -> Self { + fn max(lhs: Self, rhs: Self) -> Self { UsageDelta { - address_space: std::cmp::max( lhs.address_space, rhs.address_space ), - anonymous: std::cmp::max( lhs.anonymous, rhs.anonymous ), - shared_clean: std::cmp::max( lhs.shared_clean, rhs.shared_clean ), - shared_dirty: std::cmp::max( lhs.shared_dirty, rhs.shared_dirty ), - private_clean: std::cmp::max( lhs.private_clean, rhs.private_clean ), - private_dirty: std::cmp::max( lhs.private_dirty, rhs.private_dirty ), - swap: std::cmp::max( lhs.swap, rhs.swap ), + address_space: std::cmp::max(lhs.address_space, rhs.address_space), + anonymous: std::cmp::max(lhs.anonymous, rhs.anonymous), + shared_clean: std::cmp::max(lhs.shared_clean, rhs.shared_clean), + shared_dirty: std::cmp::max(lhs.shared_dirty, rhs.shared_dirty), + private_clean: std::cmp::max(lhs.private_clean, rhs.private_clean), + private_dirty: std::cmp::max(lhs.private_dirty, rhs.private_dirty), + swap: std::cmp::max(lhs.swap, rhs.swap), } } - fn min( lhs: Self, rhs: Self ) -> Self { + fn min(lhs: Self, rhs: Self) -> Self { UsageDelta { - address_space: std::cmp::min( lhs.address_space, rhs.address_space ), - anonymous: std::cmp::min( lhs.anonymous, rhs.anonymous ), - shared_clean: std::cmp::min( lhs.shared_clean, rhs.shared_clean ), - shared_dirty: std::cmp::min( lhs.shared_dirty, rhs.shared_dirty ), - private_clean: std::cmp::min( lhs.private_clean, rhs.private_clean ), - private_dirty: std::cmp::min( lhs.private_dirty, rhs.private_dirty ), - swap: std::cmp::min( lhs.swap, rhs.swap ), + address_space: std::cmp::min(lhs.address_space, rhs.address_space), + anonymous: std::cmp::min(lhs.anonymous, rhs.anonymous), + shared_clean: std::cmp::min(lhs.shared_clean, rhs.shared_clean), + shared_dirty: std::cmp::min(lhs.shared_dirty, rhs.shared_dirty), + private_clean: std::cmp::min(lhs.private_clean, rhs.private_clean), + private_dirty: std::cmp::min(lhs.private_dirty, rhs.private_dirty), + swap: std::cmp::min(lhs.swap, rhs.swap), } } } @@ -100,61 +102,62 @@ pub fn build_allocation_timeline( data: &Data, timestamp_min: common::Timestamp, timestamp_max: common::Timestamp, - ops: &[OperationId] -) -> Vec< TimelinePoint< AllocationDelta > > { + ops: &[OperationId], +) -> Vec> { build_timeline( timestamp_min, timestamp_max, 1000, - ops.iter().map( |op| { - let allocation = data.get_allocation( op.id() ); + ops.iter().map(|op| { + let allocation = data.get_allocation(op.id()); if op.is_allocation() { let delta = AllocationDelta { memory_usage: allocation.size as i64, - allocations: 1 + allocations: 1, }; (allocation.timestamp, delta) } else if op.is_deallocation() { let delta = AllocationDelta { memory_usage: -(allocation.size as i64), - allocations: -1 + allocations: -1, }; (allocation.deallocation.as_ref().unwrap().timestamp, delta) } else if op.is_reallocation() { - let old_allocation = data.get_allocation( allocation.reallocated_from.unwrap() ); + let old_allocation = data.get_allocation(allocation.reallocated_from.unwrap()); let delta = AllocationDelta { memory_usage: allocation.size as i64 - old_allocation.size as i64, - allocations: 0 + allocations: 0, }; (allocation.timestamp, delta) } else { unreachable!() } - }) + }), ) } pub fn build_map_timeline( timestamp_min: common::Timestamp, timestamp_max: common::Timestamp, - ops: &[(Timestamp, UsageDelta)] -) -> Vec< TimelinePoint< UsageDelta > > { - build_timeline( - timestamp_min, - timestamp_max, - 1000, - ops.iter().copied() - ) + ops: &[(Timestamp, UsageDelta)], +) -> Vec> { + build_timeline(timestamp_min, timestamp_max, 1000, ops.iter().copied()) } -fn build_timeline< T >( +fn build_timeline( timestamp_min: common::Timestamp, timestamp_max: common::Timestamp, point_count: usize, - ops: impl Iterator< Item = (Timestamp, T) > -) -> Vec< TimelinePoint< T > > where T: Delta { - let granularity = std::cmp::max( (timestamp_max - timestamp_min).as_usecs() / point_count as u64, 1 ); - let mut output = Vec::with_capacity( point_count + 2 ); + ops: impl Iterator, +) -> Vec> +where + T: Delta, +{ + let granularity = std::cmp::max( + (timestamp_max - timestamp_min).as_usecs() / point_count as u64, + 1, + ); + let mut output = Vec::with_capacity(point_count + 2); let mut current_time: u64 = 0; let mut current = T::default(); @@ -179,7 +182,7 @@ fn build_timeline< T >( positive_change: current_positive_per_time.at_least_zero(), negative_change: current_negative_per_time.at_least_zero(), }; - output.push( point ); + output.push(point); current_time += 1; current_positive_per_time = Default::default(); current_negative_per_time = Default::default(); @@ -188,28 +191,28 @@ fn build_timeline< T >( } current = next; - current_max = T::max( current_max, next ); + current_max = T::max(current_max, next); current_positive_per_time = current_positive_per_time + delta.at_least_zero(); current_negative_per_time = current_negative_per_time - delta.at_most_zero(); } if output.is_empty() { - output.push( TimelinePoint { - timestamp: (current_time * granularity).saturating_sub( 1 ), + output.push(TimelinePoint { + timestamp: (current_time * granularity).saturating_sub(1), value: Default::default(), positive_change: Default::default(), negative_change: Default::default(), }); } - output.push( TimelinePoint { + output.push(TimelinePoint { timestamp: current_time * granularity, value: current_max.at_least_zero(), positive_change: current_positive_per_time.at_least_zero(), negative_change: current_negative_per_time.at_least_zero(), }); - output.push( TimelinePoint { + output.push(TimelinePoint { timestamp: current_time * granularity + 1, value: current.at_least_zero(), positive_change: Default::default(), @@ -220,189 +223,198 @@ fn build_timeline< T >( } #[derive(PartialEq, Eq, Debug)] -pub struct TimelinePoint< T > { +pub struct TimelinePoint { pub timestamp: u64, pub value: T, pub positive_change: T, pub negative_change: T, } -impl< T > std::ops::Deref for TimelinePoint< T > { +impl std::ops::Deref for TimelinePoint { type Target = T; - fn deref( &self ) -> &Self::Target { + fn deref(&self) -> &Self::Target { &self.value } } #[test] fn test_build_timeline_one_point() { - let output = build_timeline::< i64 >( - Timestamp::from_usecs( 10000 ), - Timestamp::from_usecs( 10000 ), + let output = build_timeline::( + Timestamp::from_usecs(10000), + Timestamp::from_usecs(10000), 100, - vec![ - (Timestamp::from_usecs( 10000 ), 100) - ].into_iter() + vec![(Timestamp::from_usecs(10000), 100)].into_iter(), ); - assert_eq!( output, vec![ - TimelinePoint { - timestamp: 9999, - value: 0, - positive_change: 0, - negative_change: 0, - }, - TimelinePoint { - timestamp: 10000, - value: 100, - positive_change: 100, - negative_change: 0, - }, - TimelinePoint { - timestamp: 10001, - value: 100, - positive_change: 0, - negative_change: 0, - }, - ]); + assert_eq!( + output, + vec![ + TimelinePoint { + timestamp: 9999, + value: 0, + positive_change: 0, + negative_change: 0, + }, + TimelinePoint { + timestamp: 10000, + value: 100, + positive_change: 100, + negative_change: 0, + }, + TimelinePoint { + timestamp: 10001, + value: 100, + positive_change: 0, + negative_change: 0, + }, + ] + ); } #[test] fn test_build_timeline_oversampled() { - let output = build_timeline::< i64 >( - Timestamp::from_usecs( 0 ), - Timestamp::from_usecs( 16 ), + let output = build_timeline::( + Timestamp::from_usecs(0), + Timestamp::from_usecs(16), 100, vec![ - (Timestamp::from_usecs( 4 ), 100), - (Timestamp::from_usecs( 8 ), -25), - (Timestamp::from_usecs( 12 ), 200) - ].into_iter() + (Timestamp::from_usecs(4), 100), + (Timestamp::from_usecs(8), -25), + (Timestamp::from_usecs(12), 200), + ] + .into_iter(), ); - assert_eq!( output, vec![ - TimelinePoint { - timestamp: 4, - value: 100, - positive_change: 100, - negative_change: 0, - }, - TimelinePoint { - timestamp: 5, - value: 100, - positive_change: 0, - negative_change: 0, - }, - TimelinePoint { - timestamp: 6, - value: 100, - positive_change: 0, - negative_change: 0, - }, - TimelinePoint { - timestamp: 7, - value: 100, - positive_change: 0, - negative_change: 0, - }, - TimelinePoint { - timestamp: 8, - value: 75, - positive_change: 0, - negative_change: 25, - }, - TimelinePoint { - timestamp: 9, - value: 75, - positive_change: 0, - negative_change: 0, - }, - TimelinePoint { - timestamp: 10, - value: 75, - positive_change: 0, - negative_change: 0, - }, - TimelinePoint { - timestamp: 11, - value: 75, - positive_change: 0, - negative_change: 0, - }, - TimelinePoint { - timestamp: 12, - value: 275, - positive_change: 200, - negative_change: 0, - }, - TimelinePoint { - timestamp: 13, - value: 275, - positive_change: 0, - negative_change: 0, - }, - ]); + assert_eq!( + output, + vec![ + TimelinePoint { + timestamp: 4, + value: 100, + positive_change: 100, + negative_change: 0, + }, + TimelinePoint { + timestamp: 5, + value: 100, + positive_change: 0, + negative_change: 0, + }, + TimelinePoint { + timestamp: 6, + value: 100, + positive_change: 0, + negative_change: 0, + }, + TimelinePoint { + timestamp: 7, + value: 100, + positive_change: 0, + negative_change: 0, + }, + TimelinePoint { + timestamp: 8, + value: 75, + positive_change: 0, + negative_change: 25, + }, + TimelinePoint { + timestamp: 9, + value: 75, + positive_change: 0, + negative_change: 0, + }, + TimelinePoint { + timestamp: 10, + value: 75, + positive_change: 0, + negative_change: 0, + }, + TimelinePoint { + timestamp: 11, + value: 75, + positive_change: 0, + negative_change: 0, + }, + TimelinePoint { + timestamp: 12, + value: 275, + positive_change: 200, + negative_change: 0, + }, + TimelinePoint { + timestamp: 13, + value: 275, + positive_change: 0, + negative_change: 0, + }, + ] + ); } #[test] fn test_build_timeline_undersampled() { - let output = build_timeline::< i64 >( - Timestamp::from_usecs( 4 ), - Timestamp::from_usecs( 28 ), + let output = build_timeline::( + Timestamp::from_usecs(4), + Timestamp::from_usecs(28), 3, vec![ - (Timestamp::from_usecs( 4 ), 4), // 4 - (Timestamp::from_usecs( 5 ), 1), // 5 - (Timestamp::from_usecs( 6 ), 1), // 6 - (Timestamp::from_usecs( 7 ), 1), // 7 - (Timestamp::from_usecs( 8 ), 1), // 8 - (Timestamp::from_usecs( 9 ), 1), // 9 - (Timestamp::from_usecs( 10 ), 1), // 10 - (Timestamp::from_usecs( 11 ), 1), // 11 - (Timestamp::from_usecs( 12 ), 1), // 12 - (Timestamp::from_usecs( 13 ), -1), // 11 - (Timestamp::from_usecs( 14 ), -1), // 10 - (Timestamp::from_usecs( 15 ), -1), // 9 - (Timestamp::from_usecs( 16 ), -1), // 8 - (Timestamp::from_usecs( 17 ), -1), // 7 - (Timestamp::from_usecs( 18 ), -1), // 6 - (Timestamp::from_usecs( 19 ), -1), // 5 - (Timestamp::from_usecs( 20 ), -1), // 4 - (Timestamp::from_usecs( 21 ), 10), // 14 - (Timestamp::from_usecs( 22 ), 10), // 24 - (Timestamp::from_usecs( 23 ), 10), // 34 - (Timestamp::from_usecs( 24 ), 10), // 44 - (Timestamp::from_usecs( 25 ), 10), // 54 - (Timestamp::from_usecs( 26 ), 10), // 64 - (Timestamp::from_usecs( 27 ), 10), // 74 - (Timestamp::from_usecs( 28 ), 10), // 84 - ].into_iter() + (Timestamp::from_usecs(4), 4), // 4 + (Timestamp::from_usecs(5), 1), // 5 + (Timestamp::from_usecs(6), 1), // 6 + (Timestamp::from_usecs(7), 1), // 7 + (Timestamp::from_usecs(8), 1), // 8 + (Timestamp::from_usecs(9), 1), // 9 + (Timestamp::from_usecs(10), 1), // 10 + (Timestamp::from_usecs(11), 1), // 11 + (Timestamp::from_usecs(12), 1), // 12 + (Timestamp::from_usecs(13), -1), // 11 + (Timestamp::from_usecs(14), -1), // 10 + (Timestamp::from_usecs(15), -1), // 9 + (Timestamp::from_usecs(16), -1), // 8 + (Timestamp::from_usecs(17), -1), // 7 + (Timestamp::from_usecs(18), -1), // 6 + (Timestamp::from_usecs(19), -1), // 5 + (Timestamp::from_usecs(20), -1), // 4 + (Timestamp::from_usecs(21), 10), // 14 + (Timestamp::from_usecs(22), 10), // 24 + (Timestamp::from_usecs(23), 10), // 34 + (Timestamp::from_usecs(24), 10), // 44 + (Timestamp::from_usecs(25), 10), // 54 + (Timestamp::from_usecs(26), 10), // 64 + (Timestamp::from_usecs(27), 10), // 74 + (Timestamp::from_usecs(28), 10), // 84 + ] + .into_iter(), ); - assert_eq!( output, vec![ - TimelinePoint { - timestamp: 8, - value: 12, - positive_change: 12, - negative_change: 3, - }, - TimelinePoint { - timestamp: 16, - value: 34, - positive_change: 30, - negative_change: 5, - }, - TimelinePoint { - timestamp: 24, - value: 84, - positive_change: 50, - negative_change: 0, - }, - TimelinePoint { - timestamp: 25, - value: 84, - positive_change: 0, - negative_change: 0, - }, - ]); + assert_eq!( + output, + vec![ + TimelinePoint { + timestamp: 8, + value: 12, + positive_change: 12, + negative_change: 3, + }, + TimelinePoint { + timestamp: 16, + value: 34, + positive_change: 30, + negative_change: 5, + }, + TimelinePoint { + timestamp: 24, + value: 84, + positive_change: 50, + negative_change: 0, + }, + TimelinePoint { + timestamp: 25, + value: 84, + positive_change: 0, + negative_change: 0, + }, + ] + ); } diff --git a/cli-core/src/tree.rs b/cli-core/src/tree.rs index 8c6ddfdc..529f7b7b 100644 --- a/cli-core/src/tree.rs +++ b/cli-core/src/tree.rs @@ -1,60 +1,61 @@ -use std::cmp::{min, max}; +use std::cmp::{max, min}; use std::collections::HashMap; use std::mem::MaybeUninit; -use crate::data::{Timestamp, AllocationId, Allocation, DataPointer}; +use crate::data::{Allocation, AllocationId, DataPointer, Timestamp}; pub type NodeId = u64; -pub struct Node< K, V > { - key: MaybeUninit< K >, - value: MaybeUninit< V >, +pub struct Node { + key: MaybeUninit, + value: MaybeUninit, pub total_size: u64, pub total_count: u64, pub total_first_timestamp: Timestamp, pub total_last_timestamp: Timestamp, pub self_size: u64, pub self_count: u64, - pub self_allocations: Vec< AllocationId >, - pub children: Vec< (K, NodeId) >, - pub parent: NodeId + pub self_allocations: Vec, + pub children: Vec<(K, NodeId)>, + pub parent: NodeId, } -impl< K, V > Node< K, V > { +impl Node { #[inline] - pub fn value( &self ) -> Option< &V > { + pub fn value(&self) -> Option<&V> { if self.is_root() { None } else { - unsafe { - Some( &*self.value.as_ptr() ) - } + unsafe { Some(&*self.value.as_ptr()) } } } #[inline] - pub fn is_root( &self ) -> bool { + pub fn is_root(&self) -> bool { self.parent == -1_i64 as NodeId } } -impl< K, V > Drop for Node< K, V > { - fn drop( &mut self ) { +impl Drop for Node { + fn drop(&mut self) { if !self.is_root() { unsafe { - std::ptr::drop_in_place( self.key.as_mut_ptr() ); - std::ptr::drop_in_place( self.value.as_mut_ptr() ); + std::ptr::drop_in_place(self.key.as_mut_ptr()); + std::ptr::drop_in_place(self.value.as_mut_ptr()); } } } } -pub struct Tree< K, V > { - allocations: HashMap< DataPointer, (NodeId, usize) >, - nodes: Vec< Node< K, V > >, +pub struct Tree { + allocations: HashMap, + nodes: Vec>, } -impl< K, V > Tree< K, V > where K: PartialEq + Clone { +impl Tree +where + K: PartialEq + Clone, +{ #[inline(always)] pub fn new() -> Self { let root = Node { @@ -68,38 +69,50 @@ impl< K, V > Tree< K, V > where K: PartialEq + Clone { self_count: 0, self_allocations: Vec::new(), children: Vec::new(), - parent: -1_i64 as NodeId + parent: -1_i64 as NodeId, }; Tree { allocations: HashMap::new(), - nodes: vec![ root ] + nodes: vec![root], } } #[inline] - fn get_child_id( &self, node_id: NodeId, key: &K ) -> Option< NodeId > { - self.nodes[ node_id as usize ].children.iter().find( |&(child_key, _)| *child_key == *key ).map( |(_, child_id)| child_id ).cloned() + fn get_child_id(&self, node_id: NodeId, key: &K) -> Option { + self.nodes[node_id as usize] + .children + .iter() + .find(|&(child_key, _)| *child_key == *key) + .map(|(_, child_id)| child_id) + .cloned() } - pub fn add_allocation< T >( &mut self, allocation: &Allocation, allocation_id: AllocationId, backtrace: T ) where T: Iterator< Item = (K, V) > { + pub fn add_allocation( + &mut self, + allocation: &Allocation, + allocation_id: AllocationId, + backtrace: T, + ) where + T: Iterator, + { let timestamp = allocation.timestamp; let size = allocation.size; let mut node_id: NodeId = 0; for (key, value) in backtrace { { - let node = &mut self.nodes[ node_id as usize ]; + let node = &mut self.nodes[node_id as usize]; node.total_size += size; node.total_count += 1; - node.total_first_timestamp = min( node.total_first_timestamp, timestamp ); - node.total_last_timestamp = max( node.total_last_timestamp, timestamp ); + node.total_first_timestamp = min(node.total_first_timestamp, timestamp); + node.total_last_timestamp = max(node.total_last_timestamp, timestamp); } - let child_id = self.get_child_id( node_id, &key ); + let child_id = self.get_child_id(node_id, &key); let child_id = if child_id.is_none() { let child_node = Node { - key: MaybeUninit::new( key.clone() ), - value: MaybeUninit::new( value ), + key: MaybeUninit::new(key.clone()), + value: MaybeUninit::new(value), total_size: 0, total_count: 0, total_first_timestamp: timestamp, @@ -112,8 +125,8 @@ impl< K, V > Tree< K, V > where K: PartialEq + Clone { }; let child_id = self.nodes.len() as NodeId; - self.nodes.push( child_node ); - self.nodes[ node_id as usize ].children.push( (key, child_id) ); + self.nodes.push(child_node); + self.nodes[node_id as usize].children.push((key, child_id)); child_id } else { child_id.unwrap() @@ -122,28 +135,29 @@ impl< K, V > Tree< K, V > where K: PartialEq + Clone { node_id = child_id; } - let node = &mut self.nodes[ node_id as usize ]; + let node = &mut self.nodes[node_id as usize]; node.self_size += size; node.self_count += 1; node.total_size += size; node.total_count += 1; - node.total_first_timestamp = min( node.total_first_timestamp, timestamp ); - node.total_last_timestamp = max( node.total_last_timestamp, timestamp ); + node.total_first_timestamp = min(node.total_first_timestamp, timestamp); + node.total_last_timestamp = max(node.total_last_timestamp, timestamp); let index = node.self_allocations.len(); - self.allocations.insert( allocation.pointer, (node_id, index) ); - node.self_allocations.push( allocation_id ); + self.allocations + .insert(allocation.pointer, (node_id, index)); + node.self_allocations.push(allocation_id); } - pub fn currently_allocated( &self ) -> u64 { - self.nodes[ 0 ].total_size + pub fn currently_allocated(&self) -> u64 { + self.nodes[0].total_size } - pub fn currently_allocated_count( &self ) -> u64 { - self.nodes[ 0 ].total_count + pub fn currently_allocated_count(&self) -> u64 { + self.nodes[0].total_count } - pub fn get_node( &self, id: NodeId ) -> &Node< K, V > { - &self.nodes[ id as usize ] + pub fn get_node(&self, id: NodeId) -> &Node { + &self.nodes[id as usize] } } diff --git a/cli-core/src/tree_printer.rs b/cli-core/src/tree_printer.rs index 6441f00b..a1e24a76 100644 --- a/cli-core/src/tree_printer.rs +++ b/cli-core/src/tree_printer.rs @@ -1,70 +1,102 @@ -use crate::util::{ReadableSize, ReadableDuration}; -use crate::tree::{Tree, NodeId}; use crate::data::Timestamp; +use crate::tree::{NodeId, Tree}; +use crate::util::{ReadableDuration, ReadableSize}; -fn dump_node< K: PartialEq + Clone, V, F: Fn( &V ) -> String >( - tree: &Tree< K, V >, +fn dump_node String>( + tree: &Tree, initial_timestamp: Timestamp, printer: &mut F, node_id: NodeId, indentation: String, - stack: &mut Vec< bool >, - output: &mut Vec< Vec< String > > + stack: &mut Vec, + output: &mut Vec>, ) { if node_id == 0 { - output.push( vec![ "SIZE".to_owned(), "COUNT".to_owned(), "FIRST".to_owned(), "LAST".to_owned(), "SOURCE".to_owned() ] ); + output.push(vec![ + "SIZE".to_owned(), + "COUNT".to_owned(), + "FIRST".to_owned(), + "LAST".to_owned(), + "SOURCE".to_owned(), + ]); } let mut line = Vec::new(); let value = { - let node = tree.get_node( node_id ); + let node = tree.get_node(node_id); if node.total_count == 0 { return; } - line.push( format!( "{}", ReadableSize( node.total_size ) ) ); - line.push( format!( "{}", node.total_count ) ); - line.push( format!( "{}", ReadableDuration( (node.total_first_timestamp - initial_timestamp).as_secs() ) ) ); - line.push( format!( "{}", ReadableDuration( (node.total_last_timestamp - initial_timestamp).as_secs() ) ) ); + line.push(format!("{}", ReadableSize(node.total_size))); + line.push(format!("{}", node.total_count)); + line.push(format!( + "{}", + ReadableDuration((node.total_first_timestamp - initial_timestamp).as_secs()) + )); + line.push(format!( + "{}", + ReadableDuration((node.total_last_timestamp - initial_timestamp).as_secs()) + )); node.value() }; - if let Some( value ) = value { - line.push( format!( "{}{}", indentation, printer( value ) ) ); + if let Some(value) = value { + line.push(format!("{}{}", indentation, printer(value))); } else { - line.push( format!( "â–’" ) ); + line.push(format!("â–’")); } - output.push( line ); + output.push(line); let mut child_indentation = String::new(); for &is_last in stack.iter() { if is_last { - child_indentation.push_str( " " ); + child_indentation.push_str(" "); } else { - child_indentation.push_str( "|" ); + child_indentation.push_str("|"); } } - let children_count = tree.get_node( node_id ).children.len(); - for (index, &(_, child_id)) in tree.get_node( node_id ).children.iter().enumerate() { + let children_count = tree.get_node(node_id).children.len(); + for (index, &(_, child_id)) in tree.get_node(node_id).children.iter().enumerate() { let mut next_indentation = child_indentation.clone(); if index + 1 == children_count { - next_indentation.push_str( "â””" ); - stack.push( true ); + next_indentation.push_str("â””"); + stack.push(true); } else { - next_indentation.push_str( "├" ); - stack.push( false ); + next_indentation.push_str("├"); + stack.push(false); } - dump_node( tree, initial_timestamp, printer, child_id, next_indentation, stack, output ); + dump_node( + tree, + initial_timestamp, + printer, + child_id, + next_indentation, + stack, + output, + ); stack.pop(); } } -pub fn dump_tree< K: PartialEq + Clone, V, F: Fn( &V ) -> String >( tree: &Tree< K, V >, initial_timestamp: Timestamp, mut printer: F ) -> Vec< Vec< String > > { +pub fn dump_tree String>( + tree: &Tree, + initial_timestamp: Timestamp, + mut printer: F, +) -> Vec> { let mut stack = Vec::new(); let mut output = Vec::new(); - dump_node( tree, initial_timestamp, &mut printer, 0, "".to_owned(), &mut stack, &mut output ); + dump_node( + tree, + initial_timestamp, + &mut printer, + 0, + "".to_owned(), + &mut stack, + &mut output, + ); output } diff --git a/cli-core/src/util.rs b/cli-core/src/util.rs index 244e1346..746a6fa4 100644 --- a/cli-core/src/util.rs +++ b/cli-core/src/util.rs @@ -1,39 +1,38 @@ -use std::fmt; -use std::sync::Arc; -use std::sync::atomic::{AtomicBool, Ordering}; +use ctrlc; use std::cmp::max; +use std::fmt; use std::ops::Range; -use ctrlc; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; #[derive(Clone)] pub struct Sigint { - flag: Arc< AtomicBool > + flag: Arc, } impl Sigint { - pub fn was_sent( &self ) -> bool { - self.flag.load( Ordering::Relaxed ) + pub fn was_sent(&self) -> bool { + self.flag.load(Ordering::Relaxed) } } pub fn on_ctrlc() -> Sigint { - let aborted = Arc::new( AtomicBool::new( false ) ); + let aborted = Arc::new(AtomicBool::new(false)); { let aborted = aborted.clone(); - ctrlc::set_handler( move || { - aborted.store( true, Ordering::Relaxed ); - }).expect( "error setting Ctrl-C handler" ); + ctrlc::set_handler(move || { + aborted.store(true, Ordering::Relaxed); + }) + .expect("error setting Ctrl-C handler"); } - Sigint { - flag: aborted - } + Sigint { flag: aborted } } -pub struct ReadableDuration( pub u64 ); +pub struct ReadableDuration(pub u64); impl fmt::Display for ReadableDuration { - fn fmt( &self, formatter: &mut fmt::Formatter ) -> fmt::Result { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { let mut secs = self.0; macro_rules! get { ($mul:expr) => {{ @@ -41,38 +40,38 @@ impl fmt::Display for ReadableDuration { let out = secs / mul; secs -= out * mul; out - }} + }}; } - let days = get!( 60 * 60 * 24 ); - let hours = get!( 60 * 60 ); - let minutes = get!( 60 ); + let days = get!(60 * 60 * 24); + let hours = get!(60 * 60); + let minutes = get!(60); let show_days = days > 0; let show_hours = show_days || hours > 0; let show_minutes = show_hours || minutes > 0; if show_days { - write!( formatter, "{} days ", days )?; + write!(formatter, "{} days ", days)?; } if show_hours { - write!( formatter, "{:02}h", hours )?; + write!(formatter, "{:02}h", hours)?; } if show_minutes { - write!( formatter, "{:02}m", minutes )?; + write!(formatter, "{:02}m", minutes)?; } - write!( formatter, "{:02}s", secs )?; + write!(formatter, "{:02}s", secs)?; Ok(()) } } -pub struct ReadableSize( pub u64 ); +pub struct ReadableSize(pub u64); impl fmt::Display for ReadableSize { - fn fmt( &self, formatter: &mut fmt::Formatter ) -> fmt::Result { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { let bytes = self.0; const TB: u64 = 1000 * 1000 * 1000 * 1000; @@ -80,76 +79,81 @@ impl fmt::Display for ReadableSize { const MB: u64 = 1000 * 1000; const KB: u64 = 1000; - fn format( formatter: &mut fmt::Formatter, bytes: u64, multiplier: u64, unit: &str ) -> fmt::Result { + fn format( + formatter: &mut fmt::Formatter, + bytes: u64, + multiplier: u64, + unit: &str, + ) -> fmt::Result { let whole = bytes / multiplier; let fract = (bytes - whole * multiplier) / (multiplier / 1000); - write!( formatter, "{:3}.{:03} {}", whole, fract, unit ) + write!(formatter, "{:3}.{:03} {}", whole, fract, unit) } if bytes >= TB { - format( formatter, bytes, TB, "TB" ) + format(formatter, bytes, TB, "TB") } else if bytes >= GB { - format( formatter, bytes, GB, "GB" ) + format(formatter, bytes, GB, "GB") } else if bytes >= MB { - format( formatter, bytes, MB, "MB" ) + format(formatter, bytes, MB, "MB") } else if bytes >= KB { - format( formatter, bytes, KB, "KB" ) + format(formatter, bytes, KB, "KB") } else { - write!( formatter, "{:7}", bytes ) + write!(formatter, "{:7}", bytes) } } } #[derive(Copy, Clone, PartialEq, Eq)] -pub struct ReadableAddress( pub u64 ); +pub struct ReadableAddress(pub u64); impl fmt::Display for ReadableAddress { - fn fmt( &self, formatter: &mut fmt::Formatter ) -> fmt::Result { - write!( formatter, "{:016X}", self.0 ) + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "{:016X}", self.0) } } -pub fn table_to_string( table: &[ Vec< String > ] ) -> String { +pub fn table_to_string(table: &[Vec]) -> String { let mut output = String::new(); let mut widths = Vec::new(); let mut max_column_count = 0; for row in table { - let column_count = max( widths.len(), row.len() ); - max_column_count = max( column_count, max_column_count ); + let column_count = max(widths.len(), row.len()); + max_column_count = max(column_count, max_column_count); - widths.resize( column_count, 0 ); - for (cell, width) in row.iter().zip( widths.iter_mut() ) { - *width = max( cell.chars().count(), *width ); + widths.resize(column_count, 0); + for (cell, width) in row.iter().zip(widths.iter_mut()) { + *width = max(cell.chars().count(), *width); } } for row in table { - for (index, (cell, &width)) in row.iter().zip( widths.iter() ).enumerate() { + for (index, (cell, &width)) in row.iter().zip(widths.iter()).enumerate() { if index != 0 { - output.push_str( " " ); + output.push_str(" "); } let mut len = cell.chars().count(); - output.push_str( &cell ); + output.push_str(&cell); if index == max_column_count - 1 { continue; } while len < width { - output.push_str( " " ); + output.push_str(" "); len += 1; } } - output.push_str( "\n" ); + output.push_str("\n"); } output } -pub(crate) fn overlaps( a: Range< u64 >, b: Range< u64 > ) -> bool { +pub(crate) fn overlaps(a: Range, b: Range) -> bool { (a.end > b.start && a.end <= b.end) || // A's end is inside B (a.start >= b.start && a.start < b.end) || // A's start is inside B (b.end > a.start && b.end <= a.end) || // B's end is inside A - (b.start >= a.start && b.start < a.end) // B's start is inside A + (b.start >= a.start && b.start < a.end) // B's start is inside A } diff --git a/cli-core/src/vecvec.rs b/cli-core/src/vecvec.rs index ccca1be5..72965891 100644 --- a/cli-core/src/vecvec.rs +++ b/cli-core/src/vecvec.rs @@ -1,122 +1,144 @@ -use std::iter::FusedIterator; +use rayon::prelude::*; use std::cmp::Ordering; +use std::iter::FusedIterator; use std::u32; -use rayon::prelude::*; #[derive(Default)] -pub struct VecVec< K, T > { - index: Vec< (K, u32, u32) >, - storage: Vec< T > +pub struct VecVec { + index: Vec<(K, u32, u32)>, + storage: Vec, } -impl< K, T > VecVec< K, T > { +impl VecVec { #[inline] pub fn new() -> Self { VecVec { index: Vec::new(), - storage: Vec::new() + storage: Vec::new(), } } - pub fn shrink_to_fit( &mut self ) { + pub fn shrink_to_fit(&mut self) { self.index.shrink_to_fit(); self.storage.shrink_to_fit(); } #[inline] - pub fn insert< I: IntoIterator< Item = T > >( &mut self, key: K, iter: I ) { + pub fn insert>(&mut self, key: K, iter: I) { let offset = self.storage.len(); - self.storage.extend( iter ); + self.storage.extend(iter); let length = self.storage.len() - offset; - self.index.push( (key, offset as _, length as _) ); + self.index.push((key, offset as _, length as _)); } #[inline] - pub fn iter< 'a >( &'a self ) -> impl Iterator< Item = (&'a K, &'a [T]) > + ExactSizeIterator + FusedIterator { - self.index.iter().map( move |&(ref key, offset, length)| (key, &self.storage[ (offset as usize)..(offset + length) as usize ]) ) + pub fn iter<'a>( + &'a self, + ) -> impl Iterator + ExactSizeIterator + FusedIterator { + self.index.iter().map(move |&(ref key, offset, length)| { + ( + key, + &self.storage[(offset as usize)..(offset + length) as usize], + ) + }) } #[inline] - pub fn par_iter< 'a >( &'a self ) -> impl ParallelIterator< Item = (&'a K, &'a [T]) > where K: Send + Sync, T: Send + Sync { - self.index.par_iter().map( move |&(ref key, offset, length)| (key, &self.storage[ (offset as usize)..(offset + length) as usize ]) ) + pub fn par_iter<'a>(&'a self) -> impl ParallelIterator + where + K: Send + Sync, + T: Send + Sync, + { + self.index + .par_iter() + .map(move |&(ref key, offset, length)| { + ( + key, + &self.storage[(offset as usize)..(offset + length) as usize], + ) + }) } #[inline] - pub fn len( &self ) -> usize { + pub fn len(&self) -> usize { self.index.len() } #[inline] - pub fn get( &self, index: usize ) -> (&K, &[T]) { - let (ref key, offset, length) = self.index[ index ]; - let value = &self.storage[ (offset as usize)..(offset + length) as usize ]; + pub fn get(&self, index: usize) -> (&K, &[T]) { + let (ref key, offset, length) = self.index[index]; + let value = &self.storage[(offset as usize)..(offset + length) as usize]; (key, value) } - pub fn par_sort_by< F >( &mut self, callback: F ) - where F: Fn( (&K, &[T]), (&K, &[T]) ) -> Ordering + Send + Sync, - K: Send + Sync, - T: Send + Sync + pub fn par_sort_by(&mut self, callback: F) + where + F: Fn((&K, &[T]), (&K, &[T])) -> Ordering + Send + Sync, + K: Send + Sync, + T: Send + Sync, { let storage = &self.storage; - self.index.par_sort_by( |&(ref l_key, l_offset, l_length), &(ref r_key, r_offset, r_length)| { - let lhs = &storage[ (l_offset as usize)..(l_offset + l_length) as usize ]; - let rhs = &storage[ (r_offset as usize)..(r_offset + r_length) as usize ]; - return callback( (l_key, lhs), (r_key, rhs) ); - }) + self.index.par_sort_by( + |&(ref l_key, l_offset, l_length), &(ref r_key, r_offset, r_length)| { + let lhs = &storage[(l_offset as usize)..(l_offset + l_length) as usize]; + let rhs = &storage[(r_offset as usize)..(r_offset + r_length) as usize]; + return callback((l_key, lhs), (r_key, rhs)); + }, + ) } #[inline] - pub fn par_sort_by_key< U, F >( &mut self, callback: F ) - where F: Fn( (&K, &[T]) ) -> U + Send + Sync, - U: Ord, - K: Send + Sync, - T: Send + Sync + pub fn par_sort_by_key(&mut self, callback: F) + where + F: Fn((&K, &[T])) -> U + Send + Sync, + U: Ord, + K: Send + Sync, + T: Send + Sync, { - self.par_sort_by( |lhs, rhs| { - let lhs = callback( lhs ); - let rhs = callback( rhs ); - lhs.cmp( &rhs ) + self.par_sort_by(|lhs, rhs| { + let lhs = callback(lhs); + let rhs = callback(rhs); + lhs.cmp(&rhs) }) } - pub fn reverse( &mut self ) { + pub fn reverse(&mut self) { self.index.reverse(); } } #[derive(Default)] -pub struct DenseVecVec< T > { - index: Vec< (u32, u32) >, - storage: Vec< T > +pub struct DenseVecVec { + index: Vec<(u32, u32)>, + storage: Vec, } -impl< T > DenseVecVec< T > { +impl DenseVecVec { #[inline] pub fn new() -> Self { DenseVecVec { index: Vec::new(), - storage: Vec::new() + storage: Vec::new(), } } #[inline] - pub fn push< I: IntoIterator< Item = T > >( &mut self, iter: I ) -> usize { + pub fn push>(&mut self, iter: I) -> usize { let offset = self.storage.len(); - self.storage.extend( iter ); + self.storage.extend(iter); let length = self.storage.len() - offset; let index = self.index.len(); - self.index.push( (offset as _, length as _) ); + self.index.push((offset as _, length as _)); index } #[inline] - pub fn get( &self, index: usize ) -> &[T] { - let (offset, length) = self.index[ index ]; - &self.storage[ (offset as usize)..(offset + length) as usize ] + pub fn get(&self, index: usize) -> &[T] { + let (offset, length) = self.index[index]; + &self.storage[(offset as usize)..(offset + length) as usize] } - pub fn shrink_to_fit( &mut self ) { + pub fn shrink_to_fit(&mut self) { self.index.shrink_to_fit(); self.storage.shrink_to_fit(); } @@ -124,45 +146,45 @@ impl< T > DenseVecVec< T > { #[test] fn test_vecvec() { - let mut vec: VecVec< usize, usize > = VecVec::new(); - vec.insert( 0, vec![ 1, 2, 3 ] ); - vec.insert( 1, vec![ 10 ] ); + let mut vec: VecVec = VecVec::new(); + vec.insert(0, vec![1, 2, 3]); + vec.insert(1, vec![10]); - assert_eq!( vec.len(), 2 ); - assert_eq!( vec.iter().len(), 2 ); + assert_eq!(vec.len(), 2); + assert_eq!(vec.iter().len(), 2); { let mut iter = vec.iter(); - assert_eq!( iter.next(), Some( (&0, &[ 1, 2, 3 ][..]) ) ); - assert_eq!( iter.next(), Some( (&1, &[ 10 ][..]) ) ); - assert_eq!( iter.next(), None ); + assert_eq!(iter.next(), Some((&0, &[1, 2, 3][..]))); + assert_eq!(iter.next(), Some((&1, &[10][..]))); + assert_eq!(iter.next(), None); } - vec.insert( 3, vec![ 100 ] ); - assert_eq!( vec.len(), 3 ); - assert_eq!( vec.iter().len(), 3 ); + vec.insert(3, vec![100]); + assert_eq!(vec.len(), 3); + assert_eq!(vec.iter().len(), 3); { let mut iter = vec.iter(); - assert_eq!( iter.next(), Some( (&0, &[ 1, 2, 3 ][..]) ) ); - assert_eq!( iter.next(), Some( (&1, &[ 10 ][..]) ) ); - assert_eq!( iter.next(), Some( (&3, &[ 100 ][..]) ) ); - assert_eq!( iter.next(), None ); + assert_eq!(iter.next(), Some((&0, &[1, 2, 3][..]))); + assert_eq!(iter.next(), Some((&1, &[10][..]))); + assert_eq!(iter.next(), Some((&3, &[100][..]))); + assert_eq!(iter.next(), None); } - vec.par_sort_by_key( |(&key, _)| key ); + vec.par_sort_by_key(|(&key, _)| key); { let mut iter = vec.iter(); - assert_eq!( iter.next(), Some( (&0, &[ 1, 2, 3 ][..]) ) ); - assert_eq!( iter.next(), Some( (&1, &[ 10 ][..]) ) ); - assert_eq!( iter.next(), Some( (&3, &[ 100 ][..]) ) ); - assert_eq!( iter.next(), None ); + assert_eq!(iter.next(), Some((&0, &[1, 2, 3][..]))); + assert_eq!(iter.next(), Some((&1, &[10][..]))); + assert_eq!(iter.next(), Some((&3, &[100][..]))); + assert_eq!(iter.next(), None); } - vec.par_sort_by_key( |(&key, _)| 100 - key ); + vec.par_sort_by_key(|(&key, _)| 100 - key); { let mut iter = vec.iter(); - assert_eq!( iter.next(), Some( (&3, &[ 100 ][..]) ) ); - assert_eq!( iter.next(), Some( (&1, &[ 10 ][..]) ) ); - assert_eq!( iter.next(), Some( (&0, &[ 1, 2, 3 ][..]) ) ); - assert_eq!( iter.next(), None ); + assert_eq!(iter.next(), Some((&3, &[100][..]))); + assert_eq!(iter.next(), Some((&1, &[10][..]))); + assert_eq!(iter.next(), Some((&0, &[1, 2, 3][..]))); + assert_eq!(iter.next(), None); } } diff --git a/cli/Cargo.toml b/cli/Cargo.toml index e6a03f2b..d2e69e48 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -10,11 +10,12 @@ path = "src/main.rs" [dependencies] log = "0.4" -env_logger = "0.6" -structopt = "0.2" +env_logger = "0.10" +structopt = "0.3" cli-core = { path = "../cli-core" } server-core = { path = "../server-core", optional = true } tikv-jemallocator = "0.4" +tokio = { version = "1.26.0", features = ["macros", "rt-multi-thread"] } [features] default = ["subcommand-server"] diff --git a/cli/src/main.rs b/cli/src/main.rs index d335b61f..77814ca5 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,32 +1,25 @@ #[macro_use] extern crate log; -use std::process; use std::env; -use std::path::PathBuf; -use std::io; -use std::fs::File; use std::error::Error; - +use std::fs::File; +use std::io; +use std::path::PathBuf; +use std::process; use structopt::StructOpt; -use cli_core::{ - Anonymize, - Loader, - export_as_replay, - export_as_heaptrack, - postprocess -}; +use cli_core::{export_as_heaptrack, export_as_replay, postprocess, Anonymize, Loader}; #[global_allocator] static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; -fn parse_anonymize( source: &str ) -> Anonymize { +fn parse_anonymize(source: &str) -> Anonymize { match source { "none" => Anonymize::None, "partial" => Anonymize::Partial, "full" => Anonymize::Full, - _ => unreachable!() + _ => unreachable!(), } } @@ -38,31 +31,29 @@ enum Opt { #[structopt(short = "o", long = "output", parse(from_os_str))] output: PathBuf, #[structopt(parse(from_os_str))] - input: PathBuf + input: PathBuf, }, /// Generates a raw data file which can be loaded into heaptrack GUI #[structopt(name = "export-heaptrack")] ExportHeaptrack { /// A file or directory with extra debugging symbols; can be specified multiple times #[structopt(short = "d", long = "debug-symbols", parse(from_os_str))] - debug_symbols: Vec< PathBuf >, + debug_symbols: Vec, #[structopt(short = "o", long = "output", parse(from_os_str))] output: PathBuf, #[structopt(parse(from_os_str))] - input: PathBuf + input: PathBuf, }, /// Gathers memory tracking data from a given machine #[structopt(name = "gather")] - Gather { - target: Option< String > - }, + Gather { target: Option }, /// Launches a server with all of the data exposed through a REST API #[cfg(feature = "subcommand-server")] #[structopt(name = "server")] Server { /// A file or directory with extra debugging symbols; can be specified multiple times #[structopt(short = "d", long = "debug-symbols", parse(from_os_str))] - debug_symbols: Vec< PathBuf >, + debug_symbols: Vec, /// The network interface on which to start the HTTP server #[structopt(short = "i", long = "interface", default_value = "127.0.0.1")] interface: String, @@ -70,22 +61,27 @@ enum Opt { #[structopt(short = "p", long = "port", default_value = "8080")] port: u16, #[structopt(parse(from_os_str), required = false)] - input: Vec< PathBuf > + input: Vec, }, /// Generates a new data file with all of the stack traces decoded and deduplicated #[structopt(name = "postprocess")] Postprocess { /// A file or directory with extra debugging symbols; can be specified multiple times #[structopt(short = "d", long = "debug-symbols", parse(from_os_str))] - debug_symbols: Vec< PathBuf >, + debug_symbols: Vec, /// Whenever to anonymize the data - #[structopt(long, short = "a", parse(from_str = "parse_anonymize"), default_value="none", - raw(possible_values = r#"&[ + #[structopt( + long, + short = "a", + parse(from_str = parse_anonymize), + default_value = "none", + possible_values = &[ "none", "partial", "full" - ]"#))] + ] + )] anonymize: Anonymize, /// The file to which the postprocessed data will be written @@ -93,7 +89,7 @@ enum Opt { output: PathBuf, #[structopt(parse(from_os_str), required = false)] - input: PathBuf + input: PathBuf, }, /// Generates a new data file with temporary allocations stripped away #[structopt(name = "strip")] @@ -104,12 +100,12 @@ enum Opt { /// The minimum lifetime threshold, in seconds, of which allocations to keep #[structopt(long, short = "t")] - threshold: Option< u64 >, + threshold: Option, #[structopt(parse(from_os_str), required = false)] - input: PathBuf + input: PathBuf, }, - #[structopt(name = "repack", raw(setting = "structopt::clap::AppSettings::Hidden"))] + #[structopt(name = "repack", setting = structopt::clap::AppSettings::Hidden)] Repack { #[structopt(long)] disable_compression: bool, @@ -118,12 +114,13 @@ enum Opt { output: PathBuf, #[structopt(parse(from_os_str), required = false)] - input: PathBuf - }, - #[structopt(name = "analyze-size", raw(setting = "structopt::clap::AppSettings::Hidden"))] - AnalyzeSize { - input: PathBuf + input: PathBuf, }, + #[structopt( + name = "analyze-size", + setting = structopt::clap::AppSettings::Hidden + )] + AnalyzeSize { input: PathBuf }, /// Runs give analysis script #[structopt(name = "script")] Script { @@ -132,14 +129,17 @@ enum Opt { // Data file to load #[structopt(long, short = "d", parse(from_os_str))] - data: Option< PathBuf >, + data: Option, - args: Vec< String > + args: Vec, }, - #[structopt(name = "script-slave", raw(setting = "structopt::clap::AppSettings::Hidden"))] + #[structopt( + name = "script-slave", + setting = structopt::clap::AppSettings::Hidden + )] ScriptSlave { #[structopt(long, short = "d", parse(from_os_str))] - data: Option< PathBuf > + data: Option, }, /// Extracts all of the files embedded in the data #[structopt(name = "extract")] @@ -147,82 +147,105 @@ enum Opt { #[structopt(long, short = "o", parse(from_os_str))] output: PathBuf, input: PathBuf, - } + }, } -fn run( opt: Opt ) -> Result< (), Box< dyn Error > > { +async fn run(opt: Opt) -> Result<(), Box> { match opt { Opt::ExportReplay { output, input } => { - let fp = File::open( input )?; - let data = Loader::load_from_stream_without_debug_info( fp )?; - let data_out = File::create( output )?; - let data_out = io::BufWriter::new( data_out ); - - export_as_replay( &data, data_out, |_, _| true )?; - }, - Opt::ExportHeaptrack { debug_symbols, output, input } => { - let fp = File::open( input )?; - let data = Loader::load_from_stream( fp, debug_symbols )?; - let data_out = File::create( output )?; - let data_out = io::BufWriter::new( data_out ); - - export_as_heaptrack( &data, data_out, |_, _| true )?; - }, + let fp = File::open(input)?; + let data = Loader::load_from_stream_without_debug_info(fp)?; + let data_out = File::create(output)?; + let data_out = io::BufWriter::new(data_out); + + export_as_replay(&data, data_out, |_, _| true)?; + } + Opt::ExportHeaptrack { + debug_symbols, + output, + input, + } => { + let fp = File::open(input)?; + let data = Loader::load_from_stream(fp, debug_symbols)?; + let data_out = File::create(output)?; + let data_out = io::BufWriter::new(data_out); + + export_as_heaptrack(&data, data_out, |_, _| true)?; + } Opt::Gather { target } => { - cli_core::cmd_gather::main( target.as_ref().map( |target| target.as_str() ) )?; - }, + cli_core::cmd_gather::main(target.as_ref().map(|target| target.as_str()))?; + } #[cfg(feature = "subcommand-server")] - Opt::Server { debug_symbols, input, interface, port } => { - server_core::main( input, debug_symbols, false, &interface, port )?; - }, - Opt::Postprocess { debug_symbols, output, input, anonymize } => { - let ifp = File::open( input )?; - let ofp = File::create( output )?; - postprocess( ifp, ofp, debug_symbols, anonymize )?; - }, - Opt::Strip { output, input, threshold } => { - let ifp = File::open( &input )?; - let ofp = File::create( output )?; - cli_core::squeeze_data( ifp, ofp, threshold )?; - }, - Opt::Repack { disable_compression, input, output } => { - let ifp = File::open( &input )?; - let ofp = File::create( output )?; - cli_core::repack( disable_compression, ifp, ofp )?; - }, + Opt::Server { + debug_symbols, + input, + interface, + port, + } => { + server_core::server_main(input, debug_symbols, false, &interface, port).await?; + } + Opt::Postprocess { + debug_symbols, + output, + input, + anonymize, + } => { + let ifp = File::open(input)?; + let ofp = File::create(output)?; + postprocess(ifp, ofp, debug_symbols, anonymize)?; + } + Opt::Strip { + output, + input, + threshold, + } => { + let ifp = File::open(&input)?; + let ofp = File::create(output)?; + cli_core::squeeze_data(ifp, ofp, threshold)?; + } + Opt::Repack { + disable_compression, + input, + output, + } => { + let ifp = File::open(&input)?; + let ofp = File::create(output)?; + cli_core::repack(disable_compression, ifp, ofp)?; + } Opt::AnalyzeSize { input } => { - let ifp = File::open( &input )?; - cli_core::cmd_analyze_size::analyze_size( ifp )?; - }, + let ifp = File::open(&input)?; + cli_core::cmd_analyze_size::analyze_size(ifp)?; + } Opt::Script { input, data, args } => { - cli_core::run_script( &input, data.as_ref().map( |path| path.as_path() ), args )?; - }, + cli_core::run_script(&input, data.as_ref().map(|path| path.as_path()), args)?; + } Opt::ScriptSlave { data } => { - cli_core::script::run_script_slave( data.as_ref().map( |path| path.as_path() ) )?; - }, + cli_core::script::run_script_slave(data.as_ref().map(|path| path.as_path()))?; + } Opt::Extract { input, output } => { - cli_core::cmd_extract::extract( input, output )?; - }, + cli_core::cmd_extract::extract(input, output)?; + } } Ok(()) } -fn main() { - if env::var( "RUST_LOG" ).is_err() { - env::set_var( "RUST_LOG", "info" ); +#[tokio::main] +async fn main() { + if env::var("RUST_LOG").is_err() { + env::set_var("RUST_LOG", "info"); } env_logger::init(); let opt = Opt::from_args(); - let result = run( opt ); - if let Err( error ) = result { - error!( "{}", error ); - if !log_enabled!( log::Level::Error ) { - println!( "ERROR: {}", error ); + let result = run(opt).await; + if let Err(error) = result { + error!("{}", error); + if !log_enabled!(log::Level::Error) { + println!("ERROR: {}", error); } - process::exit( 1 ); + process::exit(1); } } diff --git a/common/Cargo.toml b/common/Cargo.toml index a21d5dc2..bab4fc47 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -9,4 +9,4 @@ lz4-compress = { path = "../lz4-compress" } speedy = "0.8" byteorder = "1" libc = "0.2" -bitflags = "1" +bitflags = "2" diff --git a/common/src/event.rs b/common/src/event.rs index 420e6f51..24f15c66 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -1,8 +1,8 @@ +use std::borrow::Cow; use std::fmt; use std::str::FromStr; -use std::borrow::Cow; -use speedy::{Readable, Writable, Context, Reader, Writer}; +use speedy::{Context, Readable, Reader, Writable, Writer}; use crate::timestamp::Timestamp; @@ -16,50 +16,50 @@ pub struct HeaderBody { pub wall_clock_secs: u64, pub wall_clock_nsecs: u64, pub pid: u32, - pub cmdline: Vec< u8 >, - pub executable: Vec< u8 >, + pub cmdline: Vec, + pub executable: Vec, pub arch: String, pub flags: u64, - pub pointer_size: u8 + pub pointer_size: u8, } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Readable, Writable)] -pub struct DataId( u64, u64 ); +pub struct DataId(u64, u64); impl DataId { - pub fn new( a: u64, b: u64 ) -> Self { - DataId( a, b ) + pub fn new(a: u64, b: u64) -> Self { + DataId(a, b) } } impl fmt::Display for DataId { - fn fmt( &self, formatter: &mut fmt::Formatter ) -> fmt::Result { - write!( formatter, "{:016x}{:016x}", self.0, self.1 ) + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "{:016x}{:016x}", self.0, self.1) } } impl FromStr for DataId { - type Err = Box< dyn std::error::Error >; + type Err = Box; - fn from_str( string: &str ) -> Result< Self, Self::Err > { + fn from_str(string: &str) -> Result { if string.len() != 32 { - return Err( "invalid ID".into() ) + return Err("invalid ID".into()); } - let id_a: u64 = u64::from_str_radix( &string[ 0..16 ], 16 )?; - let id_b: u64 = u64::from_str_radix( &string[ 16.. ], 16 )?; + let id_a: u64 = u64::from_str_radix(&string[0..16], 16)?; + let id_b: u64 = u64::from_str_radix(&string[16..], 16)?; - Ok( DataId( id_a, id_b ) ) + Ok(DataId(id_a, id_b)) } } #[test] fn test_data_id_string_conversions() { - let id_before = DataId( 0x12345678_ABCD3210, 0x9AAAAAAB_CDDDDDDF ); - assert_eq!( id_before.to_string(), "12345678abcd32109aaaaaabcddddddf" ); + let id_before = DataId(0x12345678_ABCD3210, 0x9AAAAAAB_CDDDDDDF); + assert_eq!(id_before.to_string(), "12345678abcd32109aaaaaabcddddddf"); let id_after: DataId = id_before.to_string().parse().unwrap(); - assert_eq!( id_before, id_after ); + assert_eq!(id_before, id_after); } pub const ALLOC_FLAG_JEMALLOC: u32 = 1 << 30; @@ -73,30 +73,36 @@ pub const ALLOC_FLAG_NON_MAIN_ARENA: u32 = 4; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Readable, Writable)] pub struct AllocationId { pub thread: u64, - pub allocation: u64 + pub allocation: u64, } impl std::fmt::Display for AllocationId { - fn fmt( &self, fmt: &mut std::fmt::Formatter ) -> std::fmt::Result { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { if self.is_untracked() { - write!( fmt, "{{untracked}}" ) + write!(fmt, "{{untracked}}") } else if self.is_invalid() { - write!( fmt, "{{invalid}}" ) + write!(fmt, "{{invalid}}") } else { - write!( fmt, "{{{}, {}}}", self.thread, self.allocation ) + write!(fmt, "{{{}, {}}}", self.thread, self.allocation) } } } impl AllocationId { - pub const UNTRACKED: Self = AllocationId { thread: 0, allocation: 0 }; - pub const INVALID: Self = AllocationId { thread: std::u64::MAX, allocation: std::u64::MAX }; - - pub fn is_untracked( self ) -> bool { + pub const UNTRACKED: Self = AllocationId { + thread: 0, + allocation: 0, + }; + pub const INVALID: Self = AllocationId { + thread: std::u64::MAX, + allocation: std::u64::MAX, + }; + + pub fn is_untracked(self) -> bool { self == Self::UNTRACKED } - pub fn is_invalid( self ) -> bool { + pub fn is_invalid(self) -> bool { self == Self::INVALID } } @@ -109,11 +115,11 @@ pub struct AllocBody { pub thread: u32, pub flags: u32, pub extra_usable_space: u32, - pub preceding_free_space: u64 + pub preceding_free_space: u64, } bitflags::bitflags! { - #[derive(Readable, Writable)] + #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] #[repr(transparent)] pub struct RegionFlags: u32 { const READABLE = 1 << 0; @@ -123,12 +129,23 @@ bitflags::bitflags! { } } +impl Writable for RegionFlags { + fn write_to>(&self, writer: &mut T) -> Result<(), C::Error> { + writer.write_u32(self.bits()) + } +} +impl<'a, C: Context> Readable<'a, C> for RegionFlags { + fn read_from>(reader: &mut R) -> Result::Error> { + Ok(Self::from_bits(reader.read_u32()?).unwrap()) + } +} + #[derive(Clone, PartialEq, Debug, Readable, Writable)] pub struct RegionSource { pub timestamp: Timestamp, #[speedy(varint)] pub backtrace: u64, - pub thread: u32 + pub thread: u32, } #[derive(Clone, PartialEq, Debug, Readable, Writable)] @@ -140,8 +157,8 @@ pub struct RegionTargetedSource { } #[derive(Clone, PartialEq, Debug, Readable, Writable)] -pub enum Event< 'a > { - Header( HeaderBody ), +pub enum Event<'a> { + Header(HeaderBody), Alloc { timestamp: Timestamp, allocation: AllocBody, @@ -155,24 +172,24 @@ pub enum Event< 'a > { timestamp: Timestamp, pointer: u64, backtrace: u64, - thread: u32 + thread: u32, }, File { timestamp: Timestamp, - path: Cow< 'a, str >, - contents: Cow< 'a, [u8] > + path: Cow<'a, str>, + contents: Cow<'a, [u8]>, }, Backtrace { id: u64, - addresses: Cow< 'a, [u64] > + addresses: Cow<'a, [u64]>, }, MemoryDump { address: u64, length: u64, - data: Cow< 'a, [u8] > + data: Cow<'a, [u8]>, }, Marker { - value: u32 + value: u32, }, // This event is deprecated. MemoryMap { @@ -185,7 +202,7 @@ pub enum Event< 'a > { mmap_flags: u32, file_descriptor: u32, thread: u32, - offset: u64 + offset: u64, }, // This event is deprecated. MemoryUnmap { @@ -193,7 +210,7 @@ pub enum Event< 'a > { pointer: u64, length: u64, backtrace: u64, - thread: u32 + thread: u32, }, Mallopt { timestamp: Timestamp, @@ -201,25 +218,25 @@ pub enum Event< 'a > { thread: u32, param: i32, value: i32, - result: i32 + result: i32, }, Environ { - entry: Cow< 'a, [u8] > + entry: Cow<'a, [u8]>, }, WallClock { timestamp: Timestamp, sec: u64, - nsec: u64 + nsec: u64, }, PartialBacktrace { id: u64, thread: u32, frames_invalidated: FramesInvalidated, - addresses: Cow< 'a, [u64] > + addresses: Cow<'a, [u64]>, }, String { id: u32, - string: Cow< 'a, str > + string: Cow<'a, str>, }, DecodedFrame { address: u64, @@ -229,10 +246,10 @@ pub enum Event< 'a > { source: u32, line: u32, column: u32, - is_inline: bool + is_inline: bool, }, DecodedBacktrace { - frames: Cow< 'a, [u32] > + frames: Cow<'a, [u32]>, }, GroupStatistics { backtrace: u64, @@ -241,17 +258,17 @@ pub enum Event< 'a > { free_count: u64, free_size: u64, min_size: u64, - max_size: u64 + max_size: u64, }, PartialBacktrace32 { id: u64, thread: u32, frames_invalidated: FramesInvalidated, - addresses: Cow< 'a, [u32] > + addresses: Cow<'a, [u32]>, }, Backtrace32 { id: u64, - addresses: Cow< 'a, [u32] > + addresses: Cow<'a, [u32]>, }, AllocEx { id: AllocationId, @@ -269,13 +286,13 @@ pub enum Event< 'a > { timestamp: Timestamp, pointer: u64, backtrace: u64, - thread: u32 + thread: u32, }, File64 { timestamp: Timestamp, - path: Cow< 'a, str >, + path: Cow<'a, str>, #[speedy(length_type = u64)] - contents: Cow< 'a, [u8] > + contents: Cow<'a, [u8]>, }, AddRegion { timestamp: Timestamp, @@ -293,7 +310,7 @@ pub enum Event< 'a > { minor: u32, flags: RegionFlags, #[speedy(length_type = u64_varint)] - name: Cow< 'a, str >, + name: Cow<'a, str>, }, RemoveRegion { timestamp: Timestamp, @@ -304,7 +321,7 @@ pub enum Event< 'a > { length: u64, #[speedy(length_type = u64_varint)] - sources: Cow< 'a, [RegionTargetedSource] >, + sources: Cow<'a, [RegionTargetedSource]>, }, UpdateRegionUsage { timestamp: Timestamp, @@ -341,79 +358,75 @@ pub enum Event< 'a > { file_descriptor: u32, #[speedy(varint)] offset: u64, - source: RegionSource - } + source: RegionSource, + }, } #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] pub enum FramesInvalidated { All, - Some( u32 ) + Some(u32), } -impl< 'a, C: Context > Readable< 'a, C > for FramesInvalidated { - fn read_from< R: Reader< 'a, C > >( reader: &mut R ) -> Result< Self, C::Error > { +impl<'a, C: Context> Readable<'a, C> for FramesInvalidated { + fn read_from>(reader: &mut R) -> Result { let frames = reader.read_u32()?; if frames == 0xFFFFFFFF { - Ok( FramesInvalidated::All ) + Ok(FramesInvalidated::All) } else { - Ok( FramesInvalidated::Some( frames ) ) + Ok(FramesInvalidated::Some(frames)) } } } -impl< C: Context > Writable< C > for FramesInvalidated { - fn write_to< T: ?Sized + Writer< C > >( &self, writer: &mut T ) -> Result< (), C::Error > { +impl Writable for FramesInvalidated { + fn write_to>(&self, writer: &mut T) -> Result<(), C::Error> { let value = match *self { FramesInvalidated::All => 0xFFFFFFFF, - FramesInvalidated::Some( value ) => value + FramesInvalidated::Some(value) => value, }; - writer.write_u32( value ) + writer.write_u32(value) } } #[derive(Debug)] -pub enum FramedEvent< 'a > { - Known( Event< 'a > ), - Unknown( Cow< 'a, [u8] > ) +pub enum FramedEvent<'a> { + Known(Event<'a>), + Unknown(Cow<'a, [u8]>), } -impl< 'a, C: Context > Readable< 'a, C > for FramedEvent< 'a > { - fn read_from< R: Reader< 'a, C > >( reader: &mut R ) -> Result< Self, C::Error > { +impl<'a, C: Context> Readable<'a, C> for FramedEvent<'a> { + fn read_from>(reader: &mut R) -> Result { let length = reader.read_u32()? as usize; - let bytes = reader.read_cow( length )?; + let bytes = reader.read_cow(length)?; match bytes { - Cow::Borrowed( bytes ) => { - match Event::read_from_buffer( &bytes ) { - Ok( event ) => Ok( FramedEvent::Known( event ) ), - Err( _ ) => Ok( FramedEvent::Unknown( Cow::Borrowed( bytes ) ) ) - } + Cow::Borrowed(bytes) => match Event::read_from_buffer(&bytes) { + Ok(event) => Ok(FramedEvent::Known(event)), + Err(_) => Ok(FramedEvent::Unknown(Cow::Borrowed(bytes))), + }, + Cow::Owned(bytes) => match Event::read_from_buffer_copying_data(&bytes) { + Ok(event) => Ok(FramedEvent::Known(event)), + Err(_) => Ok(FramedEvent::Unknown(Cow::Owned(bytes))), }, - Cow::Owned( bytes ) => { - match Event::read_from_buffer_copying_data( &bytes ) { - Ok( event ) => Ok( FramedEvent::Known( event ) ), - Err( _ ) => Ok( FramedEvent::Unknown( Cow::Owned( bytes ) ) ) - } - } } } } -impl< 'a, C: Context > Writable< C > for FramedEvent< 'a > { - fn write_to< T: ?Sized + Writer< C > >( &self, writer: &mut T ) -> Result< (), C::Error > { +impl<'a, C: Context> Writable for FramedEvent<'a> { + fn write_to>(&self, writer: &mut T) -> Result<(), C::Error> { match self { - &FramedEvent::Known( ref event ) => { - let length = Writable::< C >::bytes_needed( event )? as u32; - writer.write_u32( length )?; - writer.write_value( event )?; + &FramedEvent::Known(ref event) => { + let length = Writable::::bytes_needed(event)? as u32; + writer.write_u32(length)?; + writer.write_value(event)?; Ok(()) - }, - &FramedEvent::Unknown( ref bytes ) => { + } + &FramedEvent::Unknown(ref bytes) => { let length = bytes.len() as u32; - writer.write_u32( length )?; - writer.write_bytes( &bytes )?; + writer.write_u32(length)?; + writer.write_bytes(&bytes)?; Ok(()) } diff --git a/common/src/lz4_stream.rs b/common/src/lz4_stream.rs index d97f58a6..3c2de8d1 100644 --- a/common/src/lz4_stream.rs +++ b/common/src/lz4_stream.rs @@ -1,64 +1,69 @@ +use byteorder::{ByteOrder, LittleEndian, ReadBytesExt, WriteBytesExt}; +use lz4_compress; use std::cmp::min; use std::io::{self, Write}; -use lz4_compress; -use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian, ByteOrder}; const CHUNK_SIZE: usize = 512 * 1024; -pub struct Lz4Reader< F: io::Read > { - fp: Option< F >, - buffer: Vec< u8 >, - compressed_buffer: Vec< u8 >, - position: usize +pub struct Lz4Reader { + fp: Option, + buffer: Vec, + compressed_buffer: Vec, + position: usize, } // This doesn't matter in release mode but measurably helps in debug mode. #[inline(always)] -fn clear( vec: &mut Vec< u8 > ) { +fn clear(vec: &mut Vec) { unsafe { - vec.set_len( 0 ); + vec.set_len(0); } } -impl< F: io::Read > Lz4Reader< F > { - pub fn new( fp: F ) -> Self { +impl Lz4Reader { + pub fn new(fp: F) -> Self { Lz4Reader { - fp: Some( fp ), + fp: Some(fp), buffer: Vec::new(), compressed_buffer: Vec::new(), - position: 0 + position: 0, } } #[inline(always)] - fn read_cached( &mut self, buf: &mut [u8] ) -> usize { - let len = min( buf.len(), self.buffer.len() - self.position ); - buf[ ..len ].copy_from_slice( &self.buffer[ self.position..self.position + len ] ); + fn read_cached(&mut self, buf: &mut [u8]) -> usize { + let len = min(buf.len(), self.buffer.len() - self.position); + buf[..len].copy_from_slice(&self.buffer[self.position..self.position + len]); self.position += len; len } - fn fill_cache( &mut self ) -> io::Result< () > { + fn fill_cache(&mut self) -> io::Result<()> { let fp = self.fp.as_mut().unwrap(); let kind = fp.read_u8()?; match kind { 1 => { - let length = fp.read_u32::< LittleEndian >()? as usize; - self.compressed_buffer.reserve( length ); + let length = fp.read_u32::()? as usize; + self.compressed_buffer.reserve(length); unsafe { - self.compressed_buffer.set_len( length ); + self.compressed_buffer.set_len(length); } - fp.read_exact( &mut self.compressed_buffer[ .. ] )?; - lz4_compress::decompress_into( &self.compressed_buffer, &mut self.buffer ).map_err( |_| io::Error::new( io::ErrorKind::InvalidData, "decompression error" ) )?; - clear( &mut self.compressed_buffer ); - }, + fp.read_exact(&mut self.compressed_buffer[..])?; + lz4_compress::decompress_into(&self.compressed_buffer, &mut self.buffer).map_err( + |_| io::Error::new(io::ErrorKind::InvalidData, "decompression error"), + )?; + clear(&mut self.compressed_buffer); + } 2 => { - let _length = fp.read_u32::< LittleEndian >()?; + let _length = fp.read_u32::()?; unimplemented!(); - }, + } _ => { - return Err( io::Error::new( io::ErrorKind::InvalidData, format!( "unexpected kind" ) ) ); + return Err(io::Error::new( + io::ErrorKind::InvalidData, + format!("unexpected kind"), + )); } } @@ -66,63 +71,63 @@ impl< F: io::Read > Lz4Reader< F > { } } -impl< F: io::Read > io::Read for Lz4Reader< F > { - fn read( &mut self, buf: &mut [u8] ) -> io::Result< usize > { +impl io::Read for Lz4Reader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { if self.position < self.buffer.len() { - return Ok( self.read_cached( buf ) ); + return Ok(self.read_cached(buf)); } self.position = 0; - clear( &mut self.buffer ); + clear(&mut self.buffer); self.fill_cache()?; - Ok( self.read_cached( buf ) ) + Ok(self.read_cached(buf)) } } -pub struct Lz4Writer< F: io::Write > { - fp: Option< F >, - buffer: Vec< u8 >, - compression_buffer: Vec< u8 >, - is_compressed: bool +pub struct Lz4Writer { + fp: Option, + buffer: Vec, + compression_buffer: Vec, + is_compressed: bool, } -impl< F: io::Write > Lz4Writer< F > { - pub fn new( fp: F ) -> Self { +impl Lz4Writer { + pub fn new(fp: F) -> Self { Lz4Writer { - fp: Some( fp ), + fp: Some(fp), buffer: Vec::new(), compression_buffer: Vec::new(), - is_compressed: true + is_compressed: true, } } - pub fn disable_compression( &mut self ) -> io::Result< () > { + pub fn disable_compression(&mut self) -> io::Result<()> { self.flush()?; self.is_compressed = false; Ok(()) } - pub fn replace_inner( &mut self, fp: F ) -> io::Result< () > { + pub fn replace_inner(&mut self, fp: F) -> io::Result<()> { self.flush()?; - self.fp = Some( fp ); + self.fp = Some(fp); Ok(()) } - pub fn inner( &self ) -> &F { + pub fn inner(&self) -> &F { self.fp.as_ref().unwrap() } - pub fn inner_mut_without_flush( &mut self ) -> &mut F { + pub fn inner_mut_without_flush(&mut self) -> &mut F { self.fp.as_mut().unwrap() } - pub fn inner_mut( &mut self ) -> io::Result< &mut F > { + pub fn inner_mut(&mut self) -> io::Result<&mut F> { self.flush()?; - Ok( self.fp.as_mut().unwrap() ) + Ok(self.fp.as_mut().unwrap()) } - pub fn flush_and_reset_buffers( &mut self ) -> io::Result< () > { + pub fn flush_and_reset_buffers(&mut self) -> io::Result<()> { self.flush()?; self.buffer = Vec::new(); self.compression_buffer = Vec::new(); @@ -130,88 +135,98 @@ impl< F: io::Write > Lz4Writer< F > { Ok(()) } - pub fn into_inner( mut self ) -> io::Result< F > { + pub fn into_inner(mut self) -> io::Result { self.flush()?; - Ok( self.fp.take().unwrap() ) + Ok(self.fp.take().unwrap()) } } -fn write_compressed< T >( mut fp: T, compression_buffer: &mut Vec< u8 >, data: &[u8] ) -> io::Result< usize > where T: io::Write { - clear( compression_buffer ); - compression_buffer.reserve( CHUNK_SIZE ); - for chunk in data.chunks( CHUNK_SIZE ) { +fn write_compressed( + mut fp: T, + compression_buffer: &mut Vec, + data: &[u8], +) -> io::Result +where + T: io::Write, +{ + clear(compression_buffer); + compression_buffer.reserve(CHUNK_SIZE); + for chunk in data.chunks(CHUNK_SIZE) { unsafe { - compression_buffer.set_len( 5 ); + compression_buffer.set_len(5); } - compression_buffer[ 0 ] = 1; - lz4_compress::compress_into( chunk, compression_buffer ); + compression_buffer[0] = 1; + lz4_compress::compress_into(chunk, compression_buffer); let length = compression_buffer.len() as u32 - 5; - LittleEndian::write_u32( &mut compression_buffer[ 1..5 ], length ); - fp.write_all( &compression_buffer )?; + LittleEndian::write_u32(&mut compression_buffer[1..5], length); + fp.write_all(&compression_buffer)?; - clear( compression_buffer ); + clear(compression_buffer); } - Ok( data.len() ) + Ok(data.len()) } -fn write_uncompressed< T >( mut fp: T, data: &[u8] ) -> io::Result< usize > where T: io::Write { - fp.write_u8( 2 )?; - fp.write_u32::< LittleEndian >( data.len() as u32 )?; - let result = fp.write_all( &data ); +fn write_uncompressed(mut fp: T, data: &[u8]) -> io::Result +where + T: io::Write, +{ + fp.write_u8(2)?; + fp.write_u32::(data.len() as u32)?; + let result = fp.write_all(&data); result?; - Ok( data.len() ) + Ok(data.len()) } -impl< F: io::Write > Drop for Lz4Writer< F > { - fn drop( &mut self ) { +impl Drop for Lz4Writer { + fn drop(&mut self) { let _ = self.flush(); } } -impl< F: io::Write > io::Write for Lz4Writer< F > { - fn write( &mut self, slice: &[u8] ) -> io::Result< usize > { +impl io::Write for Lz4Writer { + fn write(&mut self, slice: &[u8]) -> io::Result { if slice.len() >= CHUNK_SIZE { self.flush()?; let mut fp = self.fp.as_mut().unwrap(); if self.is_compressed { - return write_compressed( &mut fp, &mut self.compression_buffer, &slice ); + return write_compressed(&mut fp, &mut self.compression_buffer, &slice); } else { - return write_uncompressed( &mut fp, &slice ); + return write_uncompressed(&mut fp, &slice); } } let position = self.buffer.len(); let target = self.buffer.len() + slice.len(); - self.buffer.reserve( slice.len() ); + self.buffer.reserve(slice.len()); unsafe { - self.buffer.set_len( target ); + self.buffer.set_len(target); } - self.buffer[ position..target ].copy_from_slice( slice ); + self.buffer[position..target].copy_from_slice(slice); if self.buffer.len() >= CHUNK_SIZE { self.flush()?; } - Ok( slice.len() ) + Ok(slice.len()) } - fn flush( &mut self ) -> io::Result< () > { + fn flush(&mut self) -> io::Result<()> { if self.buffer.is_empty() { return Ok(()); } let mut fp = self.fp.as_mut().unwrap(); if self.is_compressed { - write_compressed( &mut fp, &mut self.compression_buffer, &self.buffer )?; + write_compressed(&mut fp, &mut self.compression_buffer, &self.buffer)?; } else { - write_uncompressed( &mut fp, &self.buffer )?; + write_uncompressed(&mut fp, &self.buffer)?; } - clear( &mut self.buffer ); + clear(&mut self.buffer); fp.flush() } } diff --git a/common/src/os_util.rs b/common/src/os_util.rs index 4acdc0a2..2747be6e 100644 --- a/common/src/os_util.rs +++ b/common/src/os_util.rs @@ -1,15 +1,15 @@ +use std::mem; use std::net::IpAddr; use std::ptr; -use std::mem; use libc; #[cfg(unix)] -pub fn get_local_ips() -> Vec< IpAddr > { +pub fn get_local_ips() -> Vec { let mut output = Vec::new(); let mut head: *mut libc::ifaddrs = ptr::null_mut(); unsafe { - if libc::getifaddrs( &mut head ) < 0 { + if libc::getifaddrs(&mut head) < 0 { return output; } } @@ -20,12 +20,12 @@ pub fn get_local_ips() -> Vec< IpAddr > { if (*p).ifa_addr != ptr::null_mut() { let addr = &*(*p).ifa_addr; if addr.sa_family == libc::AF_INET as _ { - let addr: &libc::sockaddr_in = mem::transmute( addr ); + let addr: &libc::sockaddr_in = mem::transmute(addr); let mut addr = addr.sin_addr.s_addr; - if cfg!( target_endian = "little" ) { + if cfg!(target_endian = "little") { addr = addr.swap_bytes(); } - output.push( IpAddr::V4( addr.into() ) ); + output.push(IpAddr::V4(addr.into())); } } p = (*p).ifa_next; @@ -33,13 +33,13 @@ pub fn get_local_ips() -> Vec< IpAddr > { } unsafe { - libc::freeifaddrs( head ); + libc::freeifaddrs(head); } output } #[cfg(not(unix))] -pub fn get_local_ips() -> Vec< IpAddr > { +pub fn get_local_ips() -> Vec { Vec::new() } diff --git a/common/src/request.rs b/common/src/request.rs index ac28e698..c35c9c8b 100644 --- a/common/src/request.rs +++ b/common/src/request.rs @@ -1,7 +1,7 @@ -use std::borrow::Cow; -use speedy::{Readable, Writable}; -use crate::timestamp::Timestamp; use crate::event::DataId; +use crate::timestamp::Timestamp; +use speedy::{Readable, Writable}; +use std::borrow::Cow; pub const PROTOCOL_VERSION: u32 = 2; @@ -9,16 +9,16 @@ pub const PROTOCOL_VERSION: u32 = 2; pub enum Request { StartStreaming, TriggerMemoryDump, - Ping + Ping, } #[derive(PartialEq, Debug, Readable, Writable)] -pub enum Response< 'a > { - Start( BroadcastHeader ), - Data( Cow< 'a, [u8] > ), +pub enum Response<'a> { + Start(BroadcastHeader), + Data(Cow<'a, [u8]>), FinishedInitialStreaming, Pong, - Finished + Finished, } #[derive(PartialEq, Debug, Readable, Writable)] @@ -29,9 +29,9 @@ pub struct BroadcastHeader { pub wall_clock_secs: u64, pub wall_clock_nsecs: u64, pub pid: u32, - pub cmdline: Vec< u8 >, - pub executable: Vec< u8 >, + pub cmdline: Vec, + pub executable: Vec, pub arch: String, pub listener_port: u16, - pub protocol_version: u32 + pub protocol_version: u32, } diff --git a/common/src/timestamp.rs b/common/src/timestamp.rs index fd04a2f5..f9185d93 100644 --- a/common/src/timestamp.rs +++ b/common/src/timestamp.rs @@ -1,15 +1,15 @@ -use std::ops::{Add, Sub, Mul, Div}; use speedy::{Readable, Writable}; +use std::ops::{Add, Div, Mul, Sub}; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Readable, Writable)] -pub struct Timestamp( u64 ); +pub struct Timestamp(u64); impl Add for Timestamp { type Output = Timestamp; #[inline] - fn add( self, rhs: Timestamp ) -> Self::Output { - Timestamp( self.0 + rhs.0 ) + fn add(self, rhs: Timestamp) -> Self::Output { + Timestamp(self.0 + rhs.0) } } @@ -17,116 +17,116 @@ impl Sub for Timestamp { type Output = Timestamp; #[inline] - fn sub( self, rhs: Timestamp ) -> Self::Output { - Timestamp( self.0 - rhs.0 ) + fn sub(self, rhs: Timestamp) -> Self::Output { + Timestamp(self.0 - rhs.0) } } -impl Mul< f64 > for Timestamp { +impl Mul for Timestamp { type Output = Timestamp; #[inline] - fn mul( self, rhs: f64 ) -> Self::Output { - Timestamp( (self.0 as f64 * rhs) as u64 ) + fn mul(self, rhs: f64) -> Self::Output { + Timestamp((self.0 as f64 * rhs) as u64) } } -impl Div< f64 > for Timestamp { +impl Div for Timestamp { type Output = Timestamp; #[inline] - fn div( self, rhs: f64 ) -> Self::Output { - Timestamp( (self.0 as f64 / rhs) as u64 ) + fn div(self, rhs: f64) -> Self::Output { + Timestamp((self.0 as f64 / rhs) as u64) } } impl Timestamp { #[inline] - pub const fn from_secs( secs: u64 ) -> Self { - Timestamp( secs * 1_000_000 ) + pub const fn from_secs(secs: u64) -> Self { + Timestamp(secs * 1_000_000) } #[inline] - pub fn from_msecs( msecs: u64 ) -> Self { - Timestamp( msecs * 1_000 ) + pub fn from_msecs(msecs: u64) -> Self { + Timestamp(msecs * 1_000) } #[inline] - pub fn from_usecs( usecs: u64 ) -> Self { - Timestamp( usecs ) + pub fn from_usecs(usecs: u64) -> Self { + Timestamp(usecs) } #[inline] - pub fn from_timespec( secs: u64, fract_nsecs: u64 ) -> Self { - Timestamp::from_usecs( secs * 1_000_000 + fract_nsecs / 1_000 ) + pub fn from_timespec(secs: u64, fract_nsecs: u64) -> Self { + Timestamp::from_usecs(secs * 1_000_000 + fract_nsecs / 1_000) } #[inline] pub fn min() -> Self { - Timestamp( 0 ) + Timestamp(0) } #[inline] pub fn max() -> Self { - Timestamp( -1_i64 as u64 ) + Timestamp(-1_i64 as u64) } #[inline] pub fn eps() -> Self { - Timestamp( 1 ) + Timestamp(1) } #[inline] - pub fn as_secs( &self ) -> u64 { + pub fn as_secs(&self) -> u64 { self.0 / 1_000_000 } #[inline] - pub fn as_msecs( &self ) -> u64 { + pub fn as_msecs(&self) -> u64 { self.0 / 1_000 } #[inline] - pub fn as_usecs( &self ) -> u64 { + pub fn as_usecs(&self) -> u64 { self.0 } #[inline] - pub fn fract_nsecs( &self ) -> u64 { + pub fn fract_nsecs(&self) -> u64 { (self.as_usecs() - self.as_secs() * 1_000_000) * 1000 } } #[test] fn test_timestamp() { - let ts = Timestamp::from_timespec( 333, 987_654_321 ); - assert_eq!( ts.as_secs(), 333 ); - assert_eq!( ts.as_msecs(), 333987 ); - assert_eq!( ts.as_usecs(), 333987654 ); - assert_eq!( ts.fract_nsecs(), 987_654_000 ); + let ts = Timestamp::from_timespec(333, 987_654_321); + assert_eq!(ts.as_secs(), 333); + assert_eq!(ts.as_msecs(), 333987); + assert_eq!(ts.as_usecs(), 333987654); + assert_eq!(ts.fract_nsecs(), 987_654_000); assert_eq!( - ts - Timestamp::from_secs( 133 ), - Timestamp::from_timespec( 200, 987_654_321 ) + ts - Timestamp::from_secs(133), + Timestamp::from_timespec(200, 987_654_321) ); assert_eq!( - ts - Timestamp::from_usecs( 654 ), - Timestamp::from_timespec( 333, 987_000_321 ) + ts - Timestamp::from_usecs(654), + Timestamp::from_timespec(333, 987_000_321) ); assert_eq!( - Timestamp::from_secs( 1 ) - Timestamp::from_usecs( 500 ), - Timestamp::from_timespec( 0, 999_500_000 ) + Timestamp::from_secs(1) - Timestamp::from_usecs(500), + Timestamp::from_timespec(0, 999_500_000) ); assert_eq!( - Timestamp::from_timespec( 1, 200_300_400 ) * 2.0, - Timestamp::from_timespec( 2, 400_600_000 ) + Timestamp::from_timespec(1, 200_300_400) * 2.0, + Timestamp::from_timespec(2, 400_600_000) ); assert_eq!( - Timestamp::from_timespec( 2, 200_400_400 ) / 2.0, - Timestamp::from_timespec( 1, 100_200_000 ) + Timestamp::from_timespec(2, 200_400_400) / 2.0, + Timestamp::from_timespec(1, 100_200_000) ); } diff --git a/fast_range_map/Cargo.toml b/fast_range_map/Cargo.toml index ec2fbce0..e250462d 100644 --- a/fast_range_map/Cargo.toml +++ b/fast_range_map/Cargo.toml @@ -9,10 +9,10 @@ edition = "2021" hashbrown = { version = "0.13", features = ["raw"] } [dev-dependencies] -criterion = "0.3" -btree-range-map = "0.3.0" +criterion = "0.4" +btree-range-map = "0.5.0" oorandom = "11.1.3" -rangemap = "1.0.3" +rangemap = "1.3.0" [[bench]] name = "rangemaps" diff --git a/fast_range_map/benches/rangemaps.rs b/fast_range_map/benches/rangemaps.rs index 312f6c56..43b94fcd 100644 --- a/fast_range_map/benches/rangemaps.rs +++ b/fast_range_map/benches/rangemaps.rs @@ -1,30 +1,30 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion}; use std::ops::Range; -fn generate_random() -> Vec< (Range, u64) > { +fn generate_random() -> Vec<(Range, u64)> { let mut output = Vec::new(); - let mut rng = oorandom::Rand64::new( 1234567 ); + let mut rng = oorandom::Rand64::new(1234567); for n in 0..4096 { - let address = rng.rand_range( 0..u32::MAX as u64 / 2 ) & !(4096 - 1); - let length = rng.rand_range( 1..1024 ) * 4096; - output.push( (address..address + length, n) ); + let address = rng.rand_range(0..u32::MAX as u64 / 2) & !(4096 - 1); + let length = rng.rand_range(1..1024) * 4096; + output.push((address..address + length, n)); } output } -fn generate_sequential() -> Vec< (Range, u64) > { +fn generate_sequential() -> Vec<(Range, u64)> { let mut output = Vec::new(); - let mut rng = oorandom::Rand64::new( 1234567 ); + let mut rng = oorandom::Rand64::new(1234567); let mut address = 0; for n in 0..4096 { - let length = rng.rand_range( 1..1024 ) * 4096; - output.push( (address..address + length, n) ); + let length = rng.rand_range(1..1024) * 4096; + output.push((address..address + length, n)); address += length; } output } -fn generate_in_the_middle() -> Vec< (Range, u64) > { +fn generate_in_the_middle() -> Vec<(Range, u64)> { let mut output = Vec::new(); let mut l = 0; let mut r = u32::MAX as u64; @@ -41,38 +41,38 @@ fn generate_in_the_middle() -> Vec< (Range, u64) > { r -= 1; } odd_even = !odd_even; - output.push( (address..address + length, n) ); + output.push((address..address + length, n)); } output } -fn bench_rangemap( input: &[(Range, u64)] ) -> u64 { +fn bench_rangemap(input: &[(Range, u64)]) -> u64 { let mut c = 0; let mut map = rangemap::RangeMap::new(); for (range, value) in input { - map.insert( range.clone(), *value ); + map.insert(range.clone(), *value); c += 1; } c as u64 } -fn bench_btree_range_map( input: &[(Range, u64)] ) -> u64 { +fn bench_btree_range_map(input: &[(Range, u64)]) -> u64 { let mut c = 0; let mut map = btree_range_map::RangeMap::new(); for (range, value) in input { - map.insert( range.clone(), *value ); + map.insert(range.clone(), *value); c += 1; } c as u64 } -fn bench_fast_range_map( input: &[(Range, u64)] ) -> u64 { +fn bench_fast_range_map(input: &[(Range, u64)]) -> u64 { let mut c = 0; let mut map = fast_range_map::RangeMap::new(); for (range, value) in input { - map.insert( range.clone(), *value ); + map.insert(range.clone(), *value); c += 1; } @@ -80,21 +80,27 @@ fn bench_fast_range_map( input: &[(Range, u64)] ) -> u64 { } #[inline(never)] -fn run_benches( c: &mut Criterion, name: &str, run: fn( &[(Range, u64)] ) -> u64 ) { +fn run_benches(c: &mut Criterion, name: &str, run: fn(&[(Range, u64)]) -> u64) { let input_random = generate_random(); let input_sequential = generate_sequential(); let input_in_the_middle = generate_in_the_middle(); - c.bench_function( &format!( "{} (random)", name ), |b| b.iter( || run( black_box( &input_random ) ) ) ); - c.bench_function( &format!( "{} (sequential)", name ), |b| b.iter( || run( black_box( &input_sequential ) ) ) ); - c.bench_function( &format!( "{} (in the middle)", name ), |b| b.iter( || run( black_box( &input_in_the_middle ) ) ) ); + c.bench_function(&format!("{} (random)", name), |b| { + b.iter(|| run(black_box(&input_random))) + }); + c.bench_function(&format!("{} (sequential)", name), |b| { + b.iter(|| run(black_box(&input_sequential))) + }); + c.bench_function(&format!("{} (in the middle)", name), |b| { + b.iter(|| run(black_box(&input_in_the_middle))) + }); } -fn criterion_benchmark( c: &mut Criterion ) { - run_benches( c, "rangemap", bench_rangemap ); - run_benches( c, "btree_range_map", bench_btree_range_map ); - run_benches( c, "fast_range_map", bench_fast_range_map ); +fn criterion_benchmark(c: &mut Criterion) { + run_benches(c, "rangemap", bench_rangemap); + run_benches(c, "btree_range_map", bench_btree_range_map); + run_benches(c, "fast_range_map", bench_fast_range_map); } -criterion_group!( benches, criterion_benchmark ); -criterion_main!( benches ); +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); diff --git a/fast_range_map/src/lib.rs b/fast_range_map/src/lib.rs index 6ca6551f..916bd4d8 100644 --- a/fast_range_map/src/lib.rs +++ b/fast_range_map/src/lib.rs @@ -1,40 +1,40 @@ -use std::ops::Range; use std::collections::BTreeMap; +use std::ops::Range; // This was copied from `ahash`. #[inline(always)] -const fn folded_multiply( s: u64, by: u64 ) -> u64 { - let result = (s as u128).wrapping_mul( by as u128 ); +const fn folded_multiply(s: u64, by: u64) -> u64 { + let result = (s as u128).wrapping_mul(by as u128); ((result & 0xffff_ffff_ffff_ffff) as u64) ^ ((result >> 64) as u64) } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] #[repr(transparent)] -struct Index( u64 ); +struct Index(u64); impl Index { #[inline(always)] - fn new( value: u64 ) -> Self { - Index( value ) + fn new(value: u64) -> Self { + Index(value) } #[inline(always)] - fn get( self ) -> u64 { + fn get(self) -> u64 { self.0 } } #[derive(Clone)] -struct Node< K, V > { +struct Node { index: Index, key: K, value: V, - prev: Option< Index >, - next: Option< Index >, + prev: Option, + next: Option, } -impl< K, V > Node< K, V > { - fn hasher( &self ) -> u64 { +impl Node { + fn hasher(&self) -> u64 { self.index.get() } } @@ -43,50 +43,55 @@ macro_rules! eq { ($index:expr) => {{ let index = $index; move |node| node.index == index - }} + }}; } #[derive(Clone)] -struct LinkedHashMap< K, V > { - nodes: hashbrown::raw::RawTable< Node< K, V > >, +struct LinkedHashMap { + nodes: hashbrown::raw::RawTable>, counter: u64, - first_and_last: Option< (Index, Index) > + first_and_last: Option<(Index, Index)>, } -impl< K, V > LinkedHashMap< K, V > { +impl LinkedHashMap { pub const fn new() -> Self { Self { nodes: hashbrown::raw::RawTable::new(), counter: 0, - first_and_last: None + first_and_last: None, } } - pub fn is_empty( &self ) -> bool { + pub fn is_empty(&self) -> bool { self.nodes.is_empty() } - pub fn len( &self ) -> usize { + pub fn len(&self) -> usize { self.nodes.len() } #[inline(always)] - fn generate_index( &mut self ) -> Index { + fn generate_index(&mut self) -> Index { self.counter += 1; - Index::new( (folded_multiply( self.counter, 6364136223846793005 ) >> 32) | (self.counter << 32) ) + Index::new( + (folded_multiply(self.counter, 6364136223846793005) >> 32) | (self.counter << 32), + ) } - pub fn insert_back( &mut self, key: K, value: V ) -> Index { + pub fn insert_back(&mut self, key: K, value: V) -> Index { let index = self.generate_index(); let prev; - if let Some( (_, ref mut last_index) ) = self.first_and_last { - prev = Some( *last_index ); - self.nodes.get_mut( last_index.get(), eq!( *last_index ) ).unwrap().next = Some( index ); + if let Some((_, ref mut last_index)) = self.first_and_last { + prev = Some(*last_index); + self.nodes + .get_mut(last_index.get(), eq!(*last_index)) + .unwrap() + .next = Some(index); *last_index = index; } else { prev = None; - self.first_and_last = Some( (index, index) ); + self.first_and_last = Some((index, index)); } let node = Node { @@ -94,24 +99,27 @@ impl< K, V > LinkedHashMap< K, V > { key, value, prev, - next: None + next: None, }; - self.nodes.insert_entry( index.get(), node, Node::hasher ); + self.nodes.insert_entry(index.get(), node, Node::hasher); index } - pub fn insert_front( &mut self, key: K, value: V ) -> Index { + pub fn insert_front(&mut self, key: K, value: V) -> Index { let index = self.generate_index(); let next; - if let Some( (ref mut first_index, _) ) = self.first_and_last { - next = Some( *first_index ); - self.nodes.get_mut( first_index.get(), eq!( *first_index ) ).unwrap().prev = Some( index ); + if let Some((ref mut first_index, _)) = self.first_and_last { + next = Some(*first_index); + self.nodes + .get_mut(first_index.get(), eq!(*first_index)) + .unwrap() + .prev = Some(index); *first_index = index; } else { next = None; - self.first_and_last = Some( (index, index) ); + self.first_and_last = Some((index, index)); } let node = Node { @@ -119,153 +127,183 @@ impl< K, V > LinkedHashMap< K, V > { key, value, prev: None, - next + next, }; - self.nodes.insert_entry( index.get(), node, Node::hasher ); + self.nodes.insert_entry(index.get(), node, Node::hasher); index } - pub fn insert_before( &mut self, next_index: Index, key: K, value: V ) -> Index { - let next_node = self.nodes.get( next_index.get(), eq!( next_index ) ).expect( "provided index doesn't exist" ); - if let Some( prev_index ) = next_node.prev { + pub fn insert_before(&mut self, next_index: Index, key: K, value: V) -> Index { + let next_node = self + .nodes + .get(next_index.get(), eq!(next_index)) + .expect("provided index doesn't exist"); + if let Some(prev_index) = next_node.prev { let index = self.generate_index(); let node = Node { index, key, value, - prev: Some( prev_index ), - next: Some( next_index ) + prev: Some(prev_index), + next: Some(next_index), }; - self.nodes.insert_entry( index.get(), node, Node::hasher ); + self.nodes.insert_entry(index.get(), node, Node::hasher); - self.nodes.get_mut( prev_index.get(), eq!( prev_index ) ).unwrap().next = Some( index ); - self.nodes.get_mut( next_index.get(), eq!( next_index ) ).unwrap().prev = Some( index ); + self.nodes + .get_mut(prev_index.get(), eq!(prev_index)) + .unwrap() + .next = Some(index); + self.nodes + .get_mut(next_index.get(), eq!(next_index)) + .unwrap() + .prev = Some(index); return index; } - assert_eq!( self.first_and_last.unwrap().0, next_index ); - self.insert_front( key, value ) + assert_eq!(self.first_and_last.unwrap().0, next_index); + self.insert_front(key, value) } - pub fn insert_after( &mut self, prev_index: Index, key: K, value: V ) -> Index { - let prev_node = self.nodes.get( prev_index.get(), eq!( prev_index ) ).expect( "provided index doesn't exist" ); - if let Some( next_index ) = prev_node.next { + pub fn insert_after(&mut self, prev_index: Index, key: K, value: V) -> Index { + let prev_node = self + .nodes + .get(prev_index.get(), eq!(prev_index)) + .expect("provided index doesn't exist"); + if let Some(next_index) = prev_node.next { let index = self.generate_index(); let node = Node { index, key, value, - prev: Some( prev_index ), - next: Some( next_index ) + prev: Some(prev_index), + next: Some(next_index), }; - self.nodes.insert_entry( index.get(), node, Node::hasher ); + self.nodes.insert_entry(index.get(), node, Node::hasher); - self.nodes.get_mut( prev_index.get(), eq!( prev_index ) ).unwrap().next = Some( index ); - self.nodes.get_mut( next_index.get(), eq!( next_index ) ).unwrap().prev = Some( index ); + self.nodes + .get_mut(prev_index.get(), eq!(prev_index)) + .unwrap() + .next = Some(index); + self.nodes + .get_mut(next_index.get(), eq!(next_index)) + .unwrap() + .prev = Some(index); return index; } - assert_eq!( self.first_and_last.unwrap().1, prev_index ); - self.insert_back( key, value ) + assert_eq!(self.first_and_last.unwrap().1, prev_index); + self.insert_back(key, value) } #[inline] - pub fn remove_and_get_next_index( &mut self, index: Index ) -> (Option< Index >, (K, V)) { - let node = self.nodes.remove_entry( index.get(), eq!( index ) ).unwrap(); + pub fn remove_and_get_next_index(&mut self, index: Index) -> (Option, (K, V)) { + let node = self.nodes.remove_entry(index.get(), eq!(index)).unwrap(); let entry = (node.key, node.value); match (node.prev, node.next) { - (Some( prev_index ), Some( next_index )) => { - self.nodes.get_mut( prev_index.get(), eq!( prev_index ) ).unwrap().next = Some( next_index ); - self.nodes.get_mut( next_index.get(), eq!( next_index ) ).unwrap().prev = Some( prev_index ); - (Some( next_index ), entry) - }, + (Some(prev_index), Some(next_index)) => { + self.nodes + .get_mut(prev_index.get(), eq!(prev_index)) + .unwrap() + .next = Some(next_index); + self.nodes + .get_mut(next_index.get(), eq!(next_index)) + .unwrap() + .prev = Some(prev_index); + (Some(next_index), entry) + } (None, None) => { self.first_and_last = None; (None, entry) - }, - (Some( prev_index ), None) => { - self.nodes.get_mut( prev_index.get(), eq!( prev_index ) ).unwrap().next = None; + } + (Some(prev_index), None) => { + self.nodes + .get_mut(prev_index.get(), eq!(prev_index)) + .unwrap() + .next = None; self.first_and_last.as_mut().unwrap().1 = prev_index; (None, entry) - }, - (None, Some( next_index )) => { - self.nodes.get_mut( next_index.get(), eq!( next_index ) ).unwrap().prev = None; + } + (None, Some(next_index)) => { + self.nodes + .get_mut(next_index.get(), eq!(next_index)) + .unwrap() + .prev = None; self.first_and_last.as_mut().unwrap().0 = next_index; - (Some( next_index ), entry) + (Some(next_index), entry) } } } - pub fn next_index( &self, index: Index ) -> Option< Index > { - self.nodes.get( index.get(), eq!( index ) ).unwrap().next + pub fn next_index(&self, index: Index) -> Option { + self.nodes.get(index.get(), eq!(index)).unwrap().next } - pub fn first_index( &self ) -> Option< Index > { - self.first_and_last.map( |(first, _)| first ) + pub fn first_index(&self) -> Option { + self.first_and_last.map(|(first, _)| first) } - pub fn first_and_last_index( &self ) -> Option< (Index, Index) > { + pub fn first_and_last_index(&self) -> Option<(Index, Index)> { self.first_and_last } - pub fn get_key( &self, index: Index ) -> &K { - &self.nodes.get( index.get(), eq!( index ) ).unwrap().key + pub fn get_key(&self, index: Index) -> &K { + &self.nodes.get(index.get(), eq!(index)).unwrap().key } - pub fn get_key_mut( &mut self, index: Index ) -> &mut K { - &mut self.nodes.get_mut( index.get(), eq!( index ) ).unwrap().key + pub fn get_key_mut(&mut self, index: Index) -> &mut K { + &mut self.nodes.get_mut(index.get(), eq!(index)).unwrap().key } - pub fn get_value( &self, index: Index ) -> &V { - &self.nodes.get( index.get(), eq!( index ) ).unwrap().value + pub fn get_value(&self, index: Index) -> &V { + &self.nodes.get(index.get(), eq!(index)).unwrap().value } - pub fn get_value_mut( &mut self, index: Index ) -> &mut V { - &mut self.nodes.get_mut( index.get(), eq!( index ) ).unwrap().value + pub fn get_value_mut(&mut self, index: Index) -> &mut V { + &mut self.nodes.get_mut(index.get(), eq!(index)).unwrap().value } - pub fn get( &self, index: Index ) -> (&K, &V) { - let entry = &self.nodes.get( index.get(), eq!( index ) ).unwrap(); + pub fn get(&self, index: Index) -> (&K, &V) { + let entry = &self.nodes.get(index.get(), eq!(index)).unwrap(); (&entry.key, &entry.value) } - pub fn into_vec( mut self ) -> Vec< (K, V) > { - let mut output = Vec::with_capacity( self.len() ); + pub fn into_vec(mut self) -> Vec<(K, V)> { + let mut output = Vec::with_capacity(self.len()); let mut index_opt = self.first_index(); - while let Some( index ) = index_opt { - let node = self.nodes.remove_entry( index.get(), eq!( index ) ).unwrap(); - output.push( (node.key, node.value) ); + while let Some(index) = index_opt { + let node = self.nodes.remove_entry(index.get(), eq!(index)).unwrap(); + output.push((node.key, node.value)); index_opt = node.next; } output } - pub fn clear( &mut self ) { + pub fn clear(&mut self) { self.nodes.clear(); self.first_and_last = None; } } #[derive(Clone)] -pub struct RangeMap< V > { - map: BTreeMap< u64, Index >, - data: LinkedHashMap< Range< u64 >, V >, +pub struct RangeMap { + map: BTreeMap, + data: LinkedHashMap, V>, } -impl< V > Default for RangeMap< V > { +impl Default for RangeMap { fn default() -> Self { RangeMap { map: Default::default(), - data: LinkedHashMap::new() + data: LinkedHashMap::new(), } } } -impl< V > RangeMap< V > { +impl RangeMap { pub const fn new() -> Self { RangeMap { map: BTreeMap::new(), @@ -273,20 +311,26 @@ impl< V > RangeMap< V > { } } - pub fn is_empty( &self ) -> bool { + pub fn is_empty(&self) -> bool { self.data.is_empty() } - pub fn len( &self ) -> usize { + pub fn len(&self) -> usize { self.data.len() } - fn find_starting_index( &self, key_start: u64 ) -> Option< Index > { + fn find_starting_index(&self, key_start: u64) -> Option { // This finds the first entry where `entry.end > key.start`. - self.map.range( key_start + 1.. ).next().map( |(_, index)| *index ) + self.map + .range(key_start + 1..) + .next() + .map(|(_, index)| *index) } - fn insert_at_starting_index( &mut self, mut index_opt: Option< Index >, key: Range< u64 >, value: V ) where V: Clone { + fn insert_at_starting_index(&mut self, mut index_opt: Option, key: Range, value: V) + where + V: Clone, + { // The new key starts *before* this range ends. // All possibilities: @@ -321,15 +365,15 @@ impl< V > RangeMap< V > { loop { let index = match index_opt { - Some( index ) => index, + Some(index) => index, None => { - let new_index = self.data.insert_back( key.clone(), value ); - self.map.insert( key.end, new_index ); + let new_index = self.data.insert_back(key.clone(), value); + self.map.insert(key.end, new_index); break; } }; - let old = self.data.get_key( index ).clone(); + let old = self.data.get_key(index).clone(); if key.end <= old.start { // The new key ends *before* this range starts, so there's no overlap. // It should be inserted before this range. @@ -337,20 +381,19 @@ impl< V > RangeMap< V > { // |OOO| // |NNN| // - let new_index = self.data.insert_before( index, key.clone(), value ); - self.map.insert( key.end, new_index ); + let new_index = self.data.insert_before(index, key.clone(), value); + self.map.insert(key.end, new_index); return; } - if old.start >= key.start && old.end <= key.end { // The old range is completely covered by the new one. if old.end == key.end { // |OOO|???? (old range is not kept; fin) // |NNN| - *self.data.get_key_mut( index ) = key; - *self.data.get_value_mut( index ) = value; + *self.data.get_key_mut(index) = key; + *self.data.get_value_mut(index) = value; return; } @@ -360,10 +403,10 @@ impl< V > RangeMap< V > { // |OOO|?? (old range is not kept; continue) // |NNNNNNN| - let (next_index, _) = self.data.remove_and_get_next_index( index ); + let (next_index, _) = self.data.remove_and_get_next_index(index); index_opt = next_index; - self.map.remove( &old.end ); + self.map.remove(&old.end); continue; } @@ -374,14 +417,16 @@ impl< V > RangeMap< V > { // |NNN| // - let old_value = self.data.get_value( index ).clone(); - self.data.get_key_mut( index ).end = key.start; - self.map.remove( &old.end ); - self.map.insert( key.start, index ); - let new_index_1 = self.data.insert_after( index, key.clone(), value ); - let new_index_2 = self.data.insert_after( new_index_1, key.end..old.end, old_value ); - self.map.insert( key.end, new_index_1 ); - self.map.insert( old.end, new_index_2 ); + let old_value = self.data.get_value(index).clone(); + self.data.get_key_mut(index).end = key.start; + self.map.remove(&old.end); + self.map.insert(key.start, index); + let new_index_1 = self.data.insert_after(index, key.clone(), value); + let new_index_2 = self + .data + .insert_after(new_index_1, key.end..old.end, old_value); + self.map.insert(key.end, new_index_1); + self.map.insert(old.end, new_index_2); return; } @@ -391,20 +436,20 @@ impl< V > RangeMap< V > { // --------- // |OOOOO| (old range is kept, but is chopped at the start; fin) // |NNN| - self.data.get_key_mut( index ).start = key.end; - let new_index = self.data.insert_before( index, key.clone(), value ); - self.map.insert( key.end, new_index ); + self.data.get_key_mut(index).start = key.end; + let new_index = self.data.insert_before(index, key.clone(), value); + self.map.insert(key.end, new_index); return; } if key.end == old.end { // |OOOOO| (old range is kept, but is chopped at the end; fin) // |NNN| - self.data.get_key_mut( index ).end = key.start; - let new_index = self.data.insert_after( index, key.clone(), value ); - self.map.remove( &old.end ); - self.map.insert( key.start, index ); - self.map.insert( key.end, new_index ); + self.data.get_key_mut(index).end = key.start; + let new_index = self.data.insert_after(index, key.clone(), value); + self.map.remove(&old.end); + self.map.insert(key.start, index); + self.map.insert(key.end, new_index); return; } @@ -412,117 +457,126 @@ impl< V > RangeMap< V > { // |OOOOO|?? (old range is kept, but is chopped at the end; continue) // |NNN| - self.data.get_key_mut( index ).end = key.start; - self.map.remove( &old.end ); - self.map.insert( key.start, index ); - index_opt = self.data.next_index( index ); + self.data.get_key_mut(index).end = key.start; + self.map.remove(&old.end); + self.map.insert(key.start, index); + index_opt = self.data.next_index(index); } } - fn starting_index_for_removal( &self, key: Range< u64 > ) -> Option< Index > { + fn starting_index_for_removal(&self, key: Range) -> Option { if key.start == key.end { - return None ; + return None; } - assert!( key.start < key.end ); + assert!(key.start < key.end); let (first_index, last_index) = self.data.first_and_last_index()?; - if key.start >= self.data.get_key( last_index ).end { + if key.start >= self.data.get_key(last_index).end { return None; } - if key.end <= self.data.get_key( first_index ).start { + if key.end <= self.data.get_key(first_index).start { return None; } - self.find_starting_index( key.start ) + self.find_starting_index(key.start) } - pub fn remove< 'a >( &'a mut self, key: Range< u64 > ) -> RemoveIter< 'a, V > where V: Clone { - let index_opt = self.starting_index_for_removal( key.clone() ); + pub fn remove<'a>(&'a mut self, key: Range) -> RemoveIter<'a, V> + where + V: Clone, + { + let index_opt = self.starting_index_for_removal(key.clone()); RemoveIter { key, range_map: self, - index_opt + index_opt, } } - pub fn insert( &mut self, key: Range< u64 >, value: V ) where V: Clone { + pub fn insert(&mut self, key: Range, value: V) + where + V: Clone, + { if key.start == key.end { return; } - assert!( key.start < key.end ); + assert!(key.start < key.end); - if let Some( (first_index, last_index) ) = self.data.first_and_last_index() { - if key.start >= self.data.get_key( last_index ).end { - let index = self.data.insert_back( key.clone(), value ); - self.map.insert( key.end, index ); + if let Some((first_index, last_index)) = self.data.first_and_last_index() { + if key.start >= self.data.get_key(last_index).end { + let index = self.data.insert_back(key.clone(), value); + self.map.insert(key.end, index); return; - } else if key.end <= self.data.get_key( first_index ).start { - let index = self.data.insert_front( key.clone(), value ); - self.map.insert( key.end, index ); + } else if key.end <= self.data.get_key(first_index).start { + let index = self.data.insert_front(key.clone(), value); + self.map.insert(key.end, index); return; } - let index = self.find_starting_index( key.start ); - self.insert_at_starting_index( index, key, value ); + let index = self.find_starting_index(key.start); + self.insert_at_starting_index(index, key, value); } else { - let index = self.data.insert_back( key.clone(), value ); - self.map.insert( key.end, index ); + let index = self.data.insert_back(key.clone(), value); + self.map.insert(key.end, index); } } - pub fn from_vec( vec: Vec< (Range< u64 >, V) > ) -> Self where V: Clone { + pub fn from_vec(vec: Vec<(Range, V)>) -> Self + where + V: Clone, + { let mut map = Self::new(); - map.data.nodes.reserve( vec.len(), Node::hasher ); + map.data.nodes.reserve(vec.len(), Node::hasher); for (key, value) in vec { - map.insert( key, value ); + map.insert(key, value); } map } - pub fn into_vec( self ) -> Vec< (Range< u64 >, V) > { + pub fn into_vec(self) -> Vec<(Range, V)> { self.data.into_vec() } - pub fn get_value( &self, key: u64 ) -> Option< &V > { - self.get( key ).map( |(_, value)| value ) + pub fn get_value(&self, key: u64) -> Option<&V> { + self.get(key).map(|(_, value)| value) } - pub fn get( &self, key: u64 ) -> Option< (Range< u64 >, &V) > { - let mut iter = self.map.range( key.. ); + pub fn get(&self, key: u64) -> Option<(Range, &V)> { + let mut iter = self.map.range(key..); let mut index = *iter.next()?.1; - let mut range = self.data.get_key( index ).clone(); + let mut range = self.data.get_key(index).clone(); if key == range.end { index = *iter.next()?.1; - range = self.data.get_key( index ).clone(); + range = self.data.get_key(index).clone(); } if key >= range.start && key < range.end { - let (key, value) = self.data.get( index ); - return Some( (key.clone(), value) ); + let (key, value) = self.data.get(index); + return Some((key.clone(), value)); } None } - pub fn get_all_overlapping( &self, range: Range< u64 > ) -> impl Iterator< Item = (Range< u64 >, &V) > { - struct RangeIter< 'a, V > { - map: &'a RangeMap< V >, - index: Option< Index >, - end: u64 + pub fn get_all_overlapping(&self, range: Range) -> impl Iterator, &V)> { + struct RangeIter<'a, V> { + map: &'a RangeMap, + index: Option, + end: u64, } - impl< 'a, V > Iterator for RangeIter< 'a, V > { - type Item = (Range< u64 >, &'a V); + impl<'a, V> Iterator for RangeIter<'a, V> { + type Item = (Range, &'a V); - fn next( &mut self ) -> Option< Self::Item > { + fn next(&mut self) -> Option { let index = self.index?; - self.index = self.map.data.next_index( index ); - let (key, value) = self.map.data.get( index ); + self.index = self.map.data.next_index(index); + let (key, value) = self.map.data.get(index); if key.start < self.end { - Some( (key.clone(), value) ) + Some((key.clone(), value)) } else { self.index = None; None @@ -530,95 +584,109 @@ impl< V > RangeMap< V > { } } - let index = - if let Some( index ) = if range.start < range.end { self.find_starting_index( range.start ) } else { None } { - let starting_range = self.data.get_key( index ).clone(); - let matches = - (range.start >= starting_range.start && range.start < starting_range.end) || - (range.end > starting_range.start && range.end <= starting_range.end) || - (range.start < starting_range.start && range.end >= starting_range.end ) - ; - - if matches { - Some( index ) - } else { - None - } + let index = if let Some(index) = if range.start < range.end { + self.find_starting_index(range.start) + } else { + None + } { + let starting_range = self.data.get_key(index).clone(); + let matches = (range.start >= starting_range.start && range.start < starting_range.end) + || (range.end > starting_range.start && range.end <= starting_range.end) + || (range.start < starting_range.start && range.end >= starting_range.end); + + if matches { + Some(index) } else { None - }; + } + } else { + None + }; RangeIter { map: self, index, - end: range.end + end: range.end, } } - pub fn get_in_range( &self, range: Range< u64 > ) -> impl Iterator< Item = (Range< u64 >, &V) > { - self.get_all_overlapping( range.clone() ).map( move |(key, value)| { - (std::cmp::max( range.start, key.start )..std::cmp::min( range.end, key.end ), value) - }) + pub fn get_in_range(&self, range: Range) -> impl Iterator, &V)> { + self.get_all_overlapping(range.clone()) + .map(move |(key, value)| { + ( + std::cmp::max(range.start, key.start)..std::cmp::min(range.end, key.end), + value, + ) + }) } - pub fn values( &self ) -> impl ExactSizeIterator< Item = &V > { - struct ValuesIter< 'a, V > { - map: &'a RangeMap< V >, - index: Option< Index >, - remaining: usize + pub fn values(&self) -> impl ExactSizeIterator { + struct ValuesIter<'a, V> { + map: &'a RangeMap, + index: Option, + remaining: usize, } - impl< 'a, V > Iterator for ValuesIter< 'a, V > { + impl<'a, V> Iterator for ValuesIter<'a, V> { type Item = &'a V; - fn next( &mut self ) -> Option< Self::Item > { + fn next(&mut self) -> Option { let index = self.index?; - self.index = self.map.data.next_index( index ); - Some( self.map.data.get_value( index ) ) + self.index = self.map.data.next_index(index); + Some(self.map.data.get_value(index)) } - fn size_hint( &self ) -> (usize, Option< usize >) { - (self.remaining, Some( self.remaining )) + fn size_hint(&self) -> (usize, Option) { + (self.remaining, Some(self.remaining)) } } - impl< 'a, V > ExactSizeIterator for ValuesIter< 'a, V > {} + impl<'a, V> ExactSizeIterator for ValuesIter<'a, V> {} ValuesIter { map: self, index: self.data.first_index(), - remaining: self.data.len() + remaining: self.data.len(), } } - pub fn clear( &mut self ) { + pub fn clear(&mut self) { self.map.clear(); self.data.clear(); } } -pub struct RemoveIter< 'a, V > where V: Clone { - key: Range< u64 >, - range_map: &'a mut RangeMap< V >, - index_opt: Option< Index > +pub struct RemoveIter<'a, V> +where + V: Clone, +{ + key: Range, + range_map: &'a mut RangeMap, + index_opt: Option, } -impl< 'a, V > Drop for RemoveIter< 'a, V > where V: Clone { - fn drop( &mut self ) { - while let Some( _ ) = self.next() {} +impl<'a, V> Drop for RemoveIter<'a, V> +where + V: Clone, +{ + fn drop(&mut self) { + while let Some(_) = self.next() {} } } -impl< 'a, V > Iterator for RemoveIter< 'a, V > where V: Clone { - type Item = (Range< u64 >, V); - fn next( &mut self ) -> Option< Self::Item > { +impl<'a, V> Iterator for RemoveIter<'a, V> +where + V: Clone, +{ + type Item = (Range, V); + fn next(&mut self) -> Option { loop { let index = match self.index_opt { - Some( index ) => index, - None => return None + Some(index) => index, + None => return None, }; - let old = self.range_map.data.get_key( index ).clone(); + let old = self.range_map.data.get_key(index).clone(); if self.key.end <= old.start { // The new key ends *before* this range starts, so there's no overlap. // @@ -629,17 +697,16 @@ impl< 'a, V > Iterator for RemoveIter< 'a, V > where V: Clone { return None; } - if old.start >= self.key.start && old.end <= self.key.end { // The old range is completely covered by the new one. if old.end == self.key.end { // |OOO|???? (old range is not kept; fin) // |NNN| - let (_, entry) = self.range_map.data.remove_and_get_next_index( index ); - self.range_map.map.remove( &old.end ); + let (_, entry) = self.range_map.data.remove_and_get_next_index(index); + self.range_map.map.remove(&old.end); self.index_opt = None; - return Some( entry ); + return Some(entry); } // |OOO|???? (old range is not kept; continue) @@ -648,10 +715,10 @@ impl< 'a, V > Iterator for RemoveIter< 'a, V > where V: Clone { // |OOO|?? (old range is not kept; continue) // |NNNNNNN| - let (next_index, entry) = self.range_map.data.remove_and_get_next_index( index ); - self.range_map.map.remove( &old.end ); + let (next_index, entry) = self.range_map.data.remove_and_get_next_index(index); + self.range_map.map.remove(&old.end); self.index_opt = next_index; - return Some( entry ); + return Some(entry); } // The old range is partially covered by the new one. @@ -661,15 +728,18 @@ impl< 'a, V > Iterator for RemoveIter< 'a, V > where V: Clone { // |NNN| // - let value = self.range_map.data.get_value( index ).clone(); - self.range_map.data.get_key_mut( index ).end = self.key.start; - self.range_map.map.remove( &old.end ); - self.range_map.map.insert( self.key.start, index ); - let new_index = self.range_map.data.insert_after( index, self.key.end..old.end, value.clone() ); - self.range_map.map.insert( old.end, new_index ); + let value = self.range_map.data.get_value(index).clone(); + self.range_map.data.get_key_mut(index).end = self.key.start; + self.range_map.map.remove(&old.end); + self.range_map.map.insert(self.key.start, index); + let new_index = + self.range_map + .data + .insert_after(index, self.key.end..old.end, value.clone()); + self.range_map.map.insert(old.end, new_index); self.index_opt = None; - return Some( (self.key.clone(), value) ); + return Some((self.key.clone(), value)); } if self.key.start <= old.start && self.key.end > old.start { @@ -679,42 +749,42 @@ impl< 'a, V > Iterator for RemoveIter< 'a, V > where V: Clone { // |OOOOO| (old range is kept, but is chopped at the start; fin) // |NNN| - let old_key = self.range_map.data.get_key_mut( index ); + let old_key = self.range_map.data.get_key_mut(index); let chopped_key = old_key.start..self.key.end; old_key.start = self.key.end; - let value = self.range_map.data.get_value( index ).clone(); + let value = self.range_map.data.get_value(index).clone(); self.index_opt = None; - return Some( (chopped_key, value) ); + return Some((chopped_key, value)); } if self.key.end == old.end { // |OOOOO| (old range is kept, but is chopped at the end; fin) // |NNN| - let old_key = self.range_map.data.get_key_mut( index ); + let old_key = self.range_map.data.get_key_mut(index); let chopped_key = self.key.start..old_key.end; old_key.end = self.key.start; - self.range_map.map.remove( &old.end ); - self.range_map.map.insert( self.key.start, index ); - let value = self.range_map.data.get_value( index ).clone(); + self.range_map.map.remove(&old.end); + self.range_map.map.insert(self.key.start, index); + let value = self.range_map.data.get_value(index).clone(); self.index_opt = None; - return Some( (chopped_key, value) ); + return Some((chopped_key, value)); } // --------- // |OOOOO|?? (old range is kept, but is chopped at the end; continue) // |NNN| - let old_key = self.range_map.data.get_key_mut( index ); + let old_key = self.range_map.data.get_key_mut(index); let chopped_key = self.key.start..old_key.end; old_key.end = self.key.start; - self.range_map.map.remove( &old.end ); - self.range_map.map.insert( self.key.start, index ); - let value = self.range_map.data.get_value( index ).clone(); + self.range_map.map.remove(&old.end); + self.range_map.map.insert(self.key.start, index); + let value = self.range_map.data.get_value(index).clone(); - self.index_opt = self.range_map.data.next_index( index ); - return Some( (chopped_key, value) ); + self.index_opt = self.range_map.data.next_index(index); + return Some((chopped_key, value)); } } } @@ -722,208 +792,203 @@ impl< 'a, V > Iterator for RemoveIter< 'a, V > where V: Clone { #[test] fn test_insert_overlapping_at_the_start() { let mut map = RangeMap::new(); - map.insert( 2..10, 0 ); - map.insert( 0..4, 1 ); - assert_eq!( map.into_vec(), vec![ - ((0..4), 1), - ((4..10), 0) - ]); + map.insert(2..10, 0); + map.insert(0..4, 1); + assert_eq!(map.into_vec(), vec![((0..4), 1), ((4..10), 0)]); } #[test] fn test_insert_overlapping_at_the_end() { let mut map = RangeMap::new(); - map.insert( 0..4, 0 ); - map.insert( 2..10, 1 ); - assert_eq!( map.into_vec(), vec![ - ((0..2), 0), - ((2..10), 1) - ]); + map.insert(0..4, 0); + map.insert(2..10, 1); + assert_eq!(map.into_vec(), vec![((0..2), 0), ((2..10), 1)]); } #[test] fn test_insert_exactly_overlapping() { let mut map = RangeMap::new(); - map.insert( 2..10, 0 ); - map.insert( 2..10, 1 ); - assert_eq!( map.into_vec(), vec![ - ((2..10), 1) - ]); + map.insert(2..10, 0); + map.insert(2..10, 1); + assert_eq!(map.into_vec(), vec![((2..10), 1)]); } #[test] fn test_insert_bigger_overlapping() { let mut map = RangeMap::new(); - map.insert( 4..6, 0 ); - map.insert( 2..8, 1 ); - assert_eq!( map.into_vec(), vec![ - ((2..8), 1) - ]); + map.insert(4..6, 0); + map.insert(2..8, 1); + assert_eq!(map.into_vec(), vec![((2..8), 1)]); } #[test] fn test_insert_smaller_overlapping() { let mut map = RangeMap::new(); - map.insert( 2..8, 0 ); - map.insert( 4..6, 1 ); - assert_eq!( map.into_vec(), vec![ - ((2..4), 0), - ((4..6), 1), - ((6..8), 0), - ]); + map.insert(2..8, 0); + map.insert(4..6, 1); + assert_eq!(map.into_vec(), vec![((2..4), 0), ((4..6), 1), ((6..8), 0),]); } #[test] fn test_insert_longer_then_shorter() { let mut map = RangeMap::new(); - map.insert( 2..8, 0 ); - map.insert( 2..6, 1 ); - assert_eq!( map.into_vec(), vec![ - ((2..6), 1), - ((6..8), 0), - ]); + map.insert(2..8, 0); + map.insert(2..6, 1); + assert_eq!(map.into_vec(), vec![((2..6), 1), ((6..8), 0),]); } #[test] fn test_overlapping_two() { let mut map = RangeMap::new(); - map.insert( 4..30, 0 ); - map.insert( 8..20, 1 ); - assert_eq!( map.clone().into_vec(), vec![ - ((4..8), 0), - ((8..20), 1), - ((20..30), 0), - ]); - - map.insert( 4..16, 2 ); - assert_eq!( map.into_vec(), vec![ - ((4..16), 2), - ((16..20), 1), - ((20..30), 0), - ]); + map.insert(4..30, 0); + map.insert(8..20, 1); + assert_eq!( + map.clone().into_vec(), + vec![((4..8), 0), ((8..20), 1), ((20..30), 0),] + ); + + map.insert(4..16, 2); + assert_eq!( + map.into_vec(), + vec![((4..16), 2), ((16..20), 1), ((20..30), 0),] + ); } #[test] fn test_case_1() { let mut map = RangeMap::new(); - map.insert( 0..10, 0 ); - map.insert( 8..12, 1 ); - map.insert( 0..1, 2 ); - map.insert( 8..18, 3 ); - - assert_eq!( map.into_vec(), vec![ - ((0..1), 2), - ((1..8), 0), - ((8..18), 3), - ]); + map.insert(0..10, 0); + map.insert(8..12, 1); + map.insert(0..1, 2); + map.insert(8..18, 3); + + assert_eq!( + map.into_vec(), + vec![((0..1), 2), ((1..8), 0), ((8..18), 3),] + ); } #[test] fn test_get_value() { let mut map = RangeMap::new(); - map.insert( 1..8, 0 ); - map.insert( 10..18, 1 ); - map.insert( 18..28, 2 ); - - assert_eq!( map.get_value( 0 ).copied(), None ); - assert_eq!( map.get_value( 1 ).copied(), Some( 0 ) ); - assert_eq!( map.get_value( 2 ).copied(), Some( 0 ) ); - assert_eq!( map.get_value( 7 ).copied(), Some( 0 ) ); - assert_eq!( map.get_value( 8 ).copied(), None ); - assert_eq!( map.get_value( 9 ).copied(), None ); - assert_eq!( map.get_value( 10 ).copied(), Some( 1 ) ); - assert_eq!( map.get_value( 17 ).copied(), Some( 1 ) ); - assert_eq!( map.get_value( 18 ).copied(), Some( 2 ) ); - assert_eq!( map.get_value( 19 ).copied(), Some( 2 ) ); - assert_eq!( map.get_value( 27 ).copied(), Some( 2 ) ); - assert_eq!( map.get_value( 28 ).copied(), None ); + map.insert(1..8, 0); + map.insert(10..18, 1); + map.insert(18..28, 2); + + assert_eq!(map.get_value(0).copied(), None); + assert_eq!(map.get_value(1).copied(), Some(0)); + assert_eq!(map.get_value(2).copied(), Some(0)); + assert_eq!(map.get_value(7).copied(), Some(0)); + assert_eq!(map.get_value(8).copied(), None); + assert_eq!(map.get_value(9).copied(), None); + assert_eq!(map.get_value(10).copied(), Some(1)); + assert_eq!(map.get_value(17).copied(), Some(1)); + assert_eq!(map.get_value(18).copied(), Some(2)); + assert_eq!(map.get_value(19).copied(), Some(2)); + assert_eq!(map.get_value(27).copied(), Some(2)); + assert_eq!(map.get_value(28).copied(), None); } #[test] fn test_get_all_overlapping() { let mut map = RangeMap::new(); - map.insert( 1..8, 0 ); - map.insert( 10..18, 1 ); - map.insert( 18..28, 2 ); - - assert_eq!( map.get_all_overlapping( 0..1 ).collect::< Vec< _ > >(), vec![] ); - assert_eq!( map.get_all_overlapping( 0..2 ).collect::< Vec< _ > >(), vec![ (1..8, &0) ] ); - assert_eq!( map.get_all_overlapping( 1..8 ).collect::< Vec< _ > >(), vec![ (1..8, &0) ] ); - assert_eq!( map.get_all_overlapping( 2..7 ).collect::< Vec< _ > >(), vec![ (1..8, &0) ] ); - assert_eq!( map.get_all_overlapping( 2..2 ).collect::< Vec< _ > >(), vec![] ); - assert_eq!( map.get_all_overlapping( 1..10 ).collect::< Vec< _ > >(), vec![ (1..8, &0) ] ); - assert_eq!( map.get_all_overlapping( 7..10 ).collect::< Vec< _ > >(), vec![ (1..8, &0) ] ); - assert_eq!( map.get_all_overlapping( 8..10 ).collect::< Vec< _ > >(), vec![] ); - assert_eq!( map.get_all_overlapping( 7..11 ).collect::< Vec< _ > >(), vec![ (1..8, &0), (10..18, &1) ] ); - assert_eq!( map.get_all_overlapping( 0..100 ).collect::< Vec< _ > >(), vec![ (1..8, &0), (10..18, &1), (18..28, &2) ] ); - - assert_eq!( map.get_in_range( 0..1 ).collect::< Vec< _ > >(), vec![] ); - assert_eq!( map.get_in_range( 0..2 ).collect::< Vec< _ > >(), vec![ (1..2, &0) ] ); - assert_eq!( map.get_in_range( 1..8 ).collect::< Vec< _ > >(), vec![ (1..8, &0) ] ); - assert_eq!( map.get_in_range( 2..7 ).collect::< Vec< _ > >(), vec![ (2..7, &0) ] ); - assert_eq!( map.get_in_range( 2..2 ).collect::< Vec< _ > >(), vec![] ); - assert_eq!( map.get_in_range( 1..10 ).collect::< Vec< _ > >(), vec![ (1..8, &0) ] ); - assert_eq!( map.get_in_range( 7..10 ).collect::< Vec< _ > >(), vec![ (7..8, &0) ] ); - assert_eq!( map.get_in_range( 8..10 ).collect::< Vec< _ > >(), vec![] ); - assert_eq!( map.get_in_range( 7..11 ).collect::< Vec< _ > >(), vec![ (7..8, &0), (10..11, &1) ] ); - assert_eq!( map.get_in_range( 0..100 ).collect::< Vec< _ > >(), vec![ (1..8, &0), (10..18, &1), (18..28, &2) ] ); + map.insert(1..8, 0); + map.insert(10..18, 1); + map.insert(18..28, 2); + + assert_eq!(map.get_all_overlapping(0..1).collect::>(), vec![]); + assert_eq!( + map.get_all_overlapping(0..2).collect::>(), + vec![(1..8, &0)] + ); + assert_eq!( + map.get_all_overlapping(1..8).collect::>(), + vec![(1..8, &0)] + ); + assert_eq!( + map.get_all_overlapping(2..7).collect::>(), + vec![(1..8, &0)] + ); + assert_eq!(map.get_all_overlapping(2..2).collect::>(), vec![]); + assert_eq!( + map.get_all_overlapping(1..10).collect::>(), + vec![(1..8, &0)] + ); + assert_eq!( + map.get_all_overlapping(7..10).collect::>(), + vec![(1..8, &0)] + ); + assert_eq!(map.get_all_overlapping(8..10).collect::>(), vec![]); + assert_eq!( + map.get_all_overlapping(7..11).collect::>(), + vec![(1..8, &0), (10..18, &1)] + ); + assert_eq!( + map.get_all_overlapping(0..100).collect::>(), + vec![(1..8, &0), (10..18, &1), (18..28, &2)] + ); + + assert_eq!(map.get_in_range(0..1).collect::>(), vec![]); + assert_eq!(map.get_in_range(0..2).collect::>(), vec![(1..2, &0)]); + assert_eq!(map.get_in_range(1..8).collect::>(), vec![(1..8, &0)]); + assert_eq!(map.get_in_range(2..7).collect::>(), vec![(2..7, &0)]); + assert_eq!(map.get_in_range(2..2).collect::>(), vec![]); + assert_eq!( + map.get_in_range(1..10).collect::>(), + vec![(1..8, &0)] + ); + assert_eq!( + map.get_in_range(7..10).collect::>(), + vec![(7..8, &0)] + ); + assert_eq!(map.get_in_range(8..10).collect::>(), vec![]); + assert_eq!( + map.get_in_range(7..11).collect::>(), + vec![(7..8, &0), (10..11, &1)] + ); + assert_eq!( + map.get_in_range(0..100).collect::>(), + vec![(1..8, &0), (10..18, &1), (18..28, &2)] + ); } #[test] fn test_remove_whole_exact_match() { let mut map = RangeMap::new(); - map.insert( 5..10, 1 ); - assert_eq!( - map.remove( 5..10 ).collect::< Vec< _ > >(), - vec![ (5..10, 1) ] - ); - assert_eq!( map.into_vec(), vec![] ); + map.insert(5..10, 1); + assert_eq!(map.remove(5..10).collect::>(), vec![(5..10, 1)]); + assert_eq!(map.into_vec(), vec![]); } #[test] fn test_remove_whole_starting_earlier() { let mut map = RangeMap::new(); - map.insert( 5..10, 1 ); - assert_eq!( - map.remove( 0..10 ).collect::< Vec< _ > >(), - vec![ (5..10, 1) ] - ); - assert_eq!( map.into_vec(), vec![] ); + map.insert(5..10, 1); + assert_eq!(map.remove(0..10).collect::>(), vec![(5..10, 1)]); + assert_eq!(map.into_vec(), vec![]); } #[test] fn test_remove_whole_starting_later() { let mut map = RangeMap::new(); - map.insert( 5..10, 1 ); - assert_eq!( - map.remove( 5..15 ).collect::< Vec< _ > >(), - vec![ (5..10, 1) ] - ); - assert_eq!( map.into_vec(), vec![] ); + map.insert(5..10, 1); + assert_eq!(map.remove(5..15).collect::>(), vec![(5..10, 1)]); + assert_eq!(map.into_vec(), vec![]); } #[test] fn test_remove_whole_with_bigger_region() { let mut map = RangeMap::new(); - map.insert( 5..10, 1 ); - assert_eq!( - map.remove( 0..15 ).collect::< Vec< _ > >(), - vec![ (5..10, 1) ] - ); - assert_eq!( map.into_vec(), vec![] ); + map.insert(5..10, 1); + assert_eq!(map.remove(0..15).collect::>(), vec![(5..10, 1)]); + assert_eq!(map.into_vec(), vec![]); } #[test] fn test_remove_middle() { let mut map = RangeMap::new(); - map.insert( 0..10, 1 ); - assert_eq!( - map.remove( 4..6 ).collect::< Vec< _ > >(), - vec![ (4..6, 1) ] - ); + map.insert(0..10, 1); + assert_eq!(map.remove(4..6).collect::>(), vec![(4..6, 1)]); - assert_eq!( map.into_vec(), vec![ - ((0..4), 1), - ((6..10), 1) - ]); + assert_eq!(map.into_vec(), vec![((0..4), 1), ((6..10), 1)]); } diff --git a/gather/src/main.rs b/gather/src/main.rs index cccc1d38..aae34745 100644 --- a/gather/src/main.rs +++ b/gather/src/main.rs @@ -1,48 +1,50 @@ #[macro_use] extern crate log; -use std::process; -use clap::{Arg, App}; -use log::{Record, Level, Metadata, LevelFilter}; use chrono::Local; +use clap::{App, Arg}; +use log::{Level, LevelFilter, Metadata, Record}; +use std::process; pub struct SimpleLogger; impl log::Log for SimpleLogger { #[inline] - fn enabled( &self, metadata: &Metadata ) -> bool { + fn enabled(&self, metadata: &Metadata) -> bool { metadata.level() <= Level::Info } #[inline] - fn log( &self, record: &Record ) { - if self.enabled( record.metadata() ) { - eprintln!( "{}: {} - {}", Local::now().format( "%Y-%m-%d %H:%M:%S" ), record.level(), record.args() ); + fn log(&self, record: &Record) { + if self.enabled(record.metadata()) { + eprintln!( + "{}: {} - {}", + Local::now().format("%Y-%m-%d %H:%M:%S"), + record.level(), + record.args() + ); } } #[inline] - fn flush( &self ) {} + fn flush(&self) {} } fn main() { - log::set_logger( &SimpleLogger ).unwrap(); - log::set_max_level( LevelFilter::Info ); + log::set_logger(&SimpleLogger).unwrap(); + log::set_max_level(LevelFilter::Info); - let app = App::new( "gather" ) - .about( "Gathers memory tracking data from a given machine" ) - .arg( - Arg::with_name( "TARGET" ) - .required( false ) - ); + let app = App::new("gather") + .about("Gathers memory tracking data from a given machine") + .arg(Arg::with_name("TARGET").required(false)); let matches = app.get_matches(); - let target = matches.value_of( "TARGET" ); - let result = cli_core::cmd_gather::main( target ); + let target = matches.value_of("TARGET"); + let result = cli_core::cmd_gather::main(target); - if let Err( error ) = result { - error!( "{}", error ); - process::exit( 1 ); + if let Err(error) = result { + error!("{}", error); + process::exit(1); } } diff --git a/integration-tests/build.rs b/integration-tests/build.rs index 4572cb9b..81caa36d 100644 --- a/integration-tests/build.rs +++ b/integration-tests/build.rs @@ -1,6 +1,6 @@ fn main() { println!( "cargo:rustc-env=TARGET={}", - std::env::var( "TARGET" ).unwrap() + std::env::var("TARGET").unwrap() ); } diff --git a/integration-tests/src/tests.rs b/integration-tests/src/tests.rs index 4401a88f..fdd55804 100644 --- a/integration-tests/src/tests.rs +++ b/integration-tests/src/tests.rs @@ -1,76 +1,67 @@ use { - serde_derive::{ - Deserialize - }, + crate::utils::*, + serde_derive::Deserialize, std::{ - ffi::{ - OsString, - OsStr - }, - path::{ - Path, - PathBuf - }, - sync::{ - atomic::{ - AtomicUsize, - Ordering - } - }, + ffi::{OsStr, OsString}, + path::{Path, PathBuf}, + sync::atomic::{AtomicUsize, Ordering}, thread, - time::{ - Duration, - Instant - } + time::{Duration, Instant}, }, - crate::{ - utils::* - } }; fn repository_root() -> PathBuf { - Path::new( env!( "CARGO_MANIFEST_DIR" ) ).join( ".." ).canonicalize().unwrap() + Path::new(env!("CARGO_MANIFEST_DIR")) + .join("..") + .canonicalize() + .unwrap() } -fn target() -> Option< String > { - std::env::var( "MEMORY_PROFILER_TEST_TARGET" ).ok() +fn target() -> Option { + std::env::var("MEMORY_PROFILER_TEST_TARGET").ok() } fn build_root() -> PathBuf { - if let Some( path ) = std::env::var_os( "CARGO_TARGET_DIR" ) { + if let Some(path) = std::env::var_os("CARGO_TARGET_DIR") { let path: PathBuf = path.into(); if path.is_absolute() { path.canonicalize().unwrap() } else { - repository_root().join( path ).canonicalize().unwrap() + repository_root().join(path).canonicalize().unwrap() } } else { - repository_root().join( "target" ) + repository_root().join("target") } } fn preload_path() -> PathBuf { - let path = if let Ok( path ) = std::env::var( "MEMORY_PROFILER_TEST_PRELOAD_PATH" ) { - build_root().join( path ).join( "libbytehound.so" ) + let path = if let Ok(path) = std::env::var("MEMORY_PROFILER_TEST_PRELOAD_PATH") { + build_root().join(path).join("libbytehound.so") } else { let target = match target() { - Some( target ) => target, - None => "x86_64-unknown-linux-gnu".to_owned() + Some(target) => target, + None => "x86_64-unknown-linux-gnu".to_owned(), }; let mut potential_paths = vec![ - build_root().join( &target ).join( "debug" ).join( "libbytehound.so" ), - build_root().join( &target ).join( "release" ).join( "libbytehound.so" ) + build_root() + .join(&target) + .join("debug") + .join("libbytehound.so"), + build_root() + .join(&target) + .join("release") + .join("libbytehound.so"), ]; - if target == env!( "TARGET" ) { - potential_paths.push( build_root().join( "debug" ).join( "libbytehound.so" ) ); - potential_paths.push( build_root().join( "release" ).join( "libbytehound.so" ) ); + if target == env!("TARGET") { + potential_paths.push(build_root().join("debug").join("libbytehound.so")); + potential_paths.push(build_root().join("release").join("libbytehound.so")); } - potential_paths.retain( |path| path.exists() ); + potential_paths.retain(|path| path.exists()); if potential_paths.is_empty() { - panic!( "No libbytehound.so found!" ); + panic!("No libbytehound.so found!"); } if potential_paths.len() > 1 { @@ -80,19 +71,22 @@ fn preload_path() -> PathBuf { potential_paths.pop().unwrap() }; - assert!( path.exists(), "{:?} doesn't exist", path ); + assert!(path.exists(), "{:?} doesn't exist", path); path } fn cli_path() -> PathBuf { - let path = if let Ok( path ) = std::env::var( "MEMORY_PROFILER_TEST_CLI_PATH" ) { - build_root().join( path ).join( "bytehound" ) + let path = if let Ok(path) = std::env::var("MEMORY_PROFILER_TEST_CLI_PATH") { + build_root().join(path).join("bytehound") } else { - build_root().join( "x86_64-unknown-linux-gnu" ).join( "release" ).join( "bytehound" ) + build_root() + .join("x86_64-unknown-linux-gnu") + .join("release") + .join("bytehound") }; if !path.exists() { - panic!( "Missing CLI binary at {:?}; compile it and try again", path ); + panic!("Missing CLI binary at {:?}; compile it and try again", path); } path @@ -100,8 +94,8 @@ fn cli_path() -> PathBuf { fn target_toolchain_prefix() -> &'static str { let target = match target() { - Some( target ) => target, - None => return "".into() + Some(target) => target, + None => return "".into(), }; match target.as_str() { @@ -109,43 +103,43 @@ fn target_toolchain_prefix() -> &'static str { "armv7-unknown-linux-gnueabihf" => "arm-linux-gnueabihf-", "mips64-unknown-linux-gnuabi64" => "mips64-linux-gnuabi64-", "x86_64-unknown-linux-gnu" => "x86_64-linux-gnu-", - target => panic!( "Unknown target: '{}'", target ) + target => panic!("Unknown target: '{}'", target), } } fn compiler_cc() -> String { - format!( "{}gcc", target_toolchain_prefix() ) + format!("{}gcc", target_toolchain_prefix()) } fn compiler_cxx() -> String { - format!( "{}g++", target_toolchain_prefix() ) + format!("{}g++", target_toolchain_prefix()) } #[derive(Deserialize)] struct ResponseMetadata { pub id: String, pub executable: String, - pub architecture: String + pub architecture: String, } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, Debug, Hash)] #[serde(transparent)] -pub struct Secs( u64 ); +pub struct Secs(u64); #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, Debug, Hash)] #[serde(transparent)] -pub struct FractNanos( u32 ); +pub struct FractNanos(u32); #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, Debug)] pub struct Timeval { pub secs: Secs, - pub fract_nsecs: FractNanos + pub fract_nsecs: FractNanos, } #[derive(PartialEq, Deserialize, Debug)] pub struct Deallocation { pub timestamp: Timeval, - pub thread: u32 + pub thread: u32, } #[derive(PartialEq, Deserialize, Debug)] @@ -153,13 +147,13 @@ pub struct Frame { pub address: u64, pub address_s: String, pub count: u64, - pub library: Option< String >, - pub function: Option< String >, - pub raw_function: Option< String >, - pub source: Option< String >, - pub line: Option< u32 >, - pub column: Option< u32 >, - pub is_inline: bool + pub library: Option, + pub function: Option, + pub raw_function: Option, + pub source: Option, + pub line: Option, + pub column: Option, + pub is_inline: bool, } #[derive(PartialEq, Deserialize, Debug)] @@ -172,8 +166,8 @@ pub struct Allocation { pub thread: u32, pub size: u64, pub backtrace_id: u32, - pub deallocation: Option< Deallocation >, - pub backtrace: Vec< Frame >, + pub deallocation: Option, + pub backtrace: Vec, pub is_mmaped: bool, pub in_main_arena: bool, pub extra_space: u32, @@ -182,27 +176,27 @@ pub struct Allocation { #[derive(Deserialize, Debug)] struct ResponseAllocations { - pub allocations: Vec< Allocation >, - pub total_count: u64 + pub allocations: Vec, + pub total_count: u64, } #[derive(PartialEq, Deserialize, Debug)] pub struct MapDeallocation { pub timestamp: Timeval, - pub source: Option< MapSource >, + pub source: Option, } #[derive(PartialEq, Deserialize, Debug)] pub struct MapRegionDeallocationSource { pub address: u64, pub length: u64, - pub source: MapSource + pub source: MapSource, } #[derive(PartialEq, Deserialize, Debug)] pub struct MapRegionDeallocation { pub timestamp: Timeval, - pub sources: Vec< MapRegionDeallocationSource >, + pub sources: Vec, } #[derive(PartialEq, Deserialize, Debug)] @@ -210,7 +204,7 @@ pub struct MapSource { pub timestamp: Timeval, pub thread: u32, pub backtrace_id: u32, - pub backtrace: Vec< Frame > + pub backtrace: Vec, } #[derive(PartialEq, Deserialize, Debug)] @@ -221,7 +215,7 @@ pub struct MapRegion { pub timestamp_relative: Timeval, pub timestamp_relative_p: f32, pub size: u64, - pub deallocation: Option< MapRegionDeallocation >, + pub deallocation: Option, pub file_offset: u64, pub inode: u64, pub major: u32, @@ -252,23 +246,23 @@ pub struct Map { pub timestamp_relative: Timeval, pub timestamp_relative_p: f32, pub size: u64, - pub source: Option< MapSource >, - pub deallocation: Option< MapDeallocation >, + pub source: Option, + pub deallocation: Option, pub is_readable: bool, pub is_writable: bool, pub is_executable: bool, pub is_shared: bool, pub name: String, pub peak_rss: u64, - pub regions: Vec< MapRegion >, - pub usage_history: Vec< UsageHistory >, + pub regions: Vec, + pub usage_history: Vec, } #[derive(Deserialize, Debug)] struct ResponseMaps { - pub maps: Vec< Map >, + pub maps: Vec, #[allow(dead_code)] - pub total_count: u64 + pub total_count: u64, } #[derive(PartialEq, Deserialize, Debug)] @@ -284,7 +278,7 @@ pub struct AllocationGroupData { pub max_timestamp_relative_p: f32, pub interval: Timeval, pub leaked_count: u64, - pub allocated_count: u64 + pub allocated_count: u64, } #[derive(PartialEq, Deserialize, Debug)] @@ -292,13 +286,13 @@ pub struct AllocationGroup { pub all: AllocationGroupData, pub only_matched: AllocationGroupData, pub backtrace_id: u32, - pub backtrace: Vec< Frame > + pub backtrace: Vec, } #[derive(Deserialize, Debug)] pub struct ResponseAllocationGroups { - pub allocations: Vec< AllocationGroup >, - pub total_count: u64 + pub allocations: Vec, + pub total_count: u64, } struct Analysis { @@ -307,64 +301,88 @@ struct Analysis { response_maps: ResponseMaps, } -fn is_from_source( alloc: &Allocation, expected: &str ) -> bool { - alloc.backtrace.iter().any( |frame| { - frame.source.as_ref().map( |source| { - source.ends_with( expected ) - }).unwrap_or( false ) +fn is_from_source(alloc: &Allocation, expected: &str) -> bool { + alloc.backtrace.iter().any(|frame| { + frame + .source + .as_ref() + .map(|source| source.ends_with(expected)) + .unwrap_or(false) }) } -fn is_from_function( alloc: &Allocation, expected: &str ) -> bool { - alloc.backtrace.iter().any( |frame| { - frame.raw_function.as_ref().map( |symbol| { - symbol == expected - }).unwrap_or( false ) +fn is_from_function(alloc: &Allocation, expected: &str) -> bool { + alloc.backtrace.iter().any(|frame| { + frame + .raw_function + .as_ref() + .map(|symbol| symbol == expected) + .unwrap_or(false) }) } -fn is_from_function_fuzzy( alloc: &Allocation, expected: &str ) -> bool { - alloc.backtrace.iter().any( |frame| { - frame.raw_function.as_ref().map( |symbol| { - symbol.contains( expected ) - }).unwrap_or( false ) +fn is_from_function_fuzzy(alloc: &Allocation, expected: &str) -> bool { + alloc.backtrace.iter().any(|frame| { + frame + .raw_function + .as_ref() + .map(|symbol| symbol.contains(expected)) + .unwrap_or(false) }) } impl Analysis { - fn allocations_from_source< 'a >( &'a self, source: &'a str ) -> impl Iterator< Item = &Allocation > + 'a { - self.response.allocations.iter().filter( move |alloc| is_from_source( alloc, source ) ) + fn allocations_from_source<'a>( + &'a self, + source: &'a str, + ) -> impl Iterator + 'a { + self.response + .allocations + .iter() + .filter(move |alloc| is_from_source(alloc, source)) } } -fn assert_allocation_backtrace( alloc: &Allocation, expected: &[&str] ) { - let mut actual: Vec< _ > = alloc.backtrace.iter().map( |frame| frame.raw_function.clone().unwrap_or( String::new() ) ).collect(); +fn assert_allocation_backtrace(alloc: &Allocation, expected: &[&str]) { + let mut actual: Vec<_> = alloc + .backtrace + .iter() + .map(|frame| frame.raw_function.clone().unwrap_or(String::new())) + .collect(); actual.reverse(); - let matches = actual.len() >= expected.len() && actual.iter().zip( expected.iter() ).all( |(lhs, rhs)| lhs == rhs ); + let matches = actual.len() >= expected.len() + && actual + .iter() + .zip(expected.iter()) + .all(|(lhs, rhs)| lhs == rhs); if matches { return; } - panic!( "Unexpected backtrace!\n\nActual:\n{}\n\nExpected to start with:\n{}\n", actual.join( "\n" ), expected.join( "\n" ) ); + panic!( + "Unexpected backtrace!\n\nActual:\n{}\n\nExpected to start with:\n{}\n", + actual.join("\n"), + expected.join("\n") + ); } fn workdir() -> PathBuf { let workdir = build_root(); - std::fs::create_dir_all( &workdir ).unwrap(); + std::fs::create_dir_all(&workdir).unwrap(); workdir } -fn analyze( name: &str, path: impl AsRef< Path > ) -> Analysis { +fn analyze(name: &str, path: impl AsRef) -> Analysis { let cwd = workdir(); let path = path.as_ref(); - assert_file_exists( path ); + assert_file_exists(path); - static PORT: AtomicUsize = AtomicUsize::new( 8080 ); + static PORT: AtomicUsize = AtomicUsize::new(8080); let port = loop { - let port = PORT.fetch_add( 1, Ordering::SeqCst ); - if std::net::TcpListener::bind( format!( "127.0.0.1:{}", port ) ).is_ok() { + let port = PORT.fetch_add(1, Ordering::SeqCst); + if std::net::TcpListener::bind(format!("127.0.0.1:{}", port)).is_ok() { break port; } }; @@ -372,100 +390,158 @@ fn analyze( name: &str, path: impl AsRef< Path > ) -> Analysis { let _child = run_in_the_background( &cwd, cli_path(), - &[OsString::from( "server" ), path.as_os_str().to_owned(), OsString::from( "--port" ), OsString::from( format!( "{}", port ) )], - &[("RUST_LOG", "server_core=debug,cli_core=debug,actix_net=info")] + &[ + OsString::from("server"), + path.as_os_str().to_owned(), + OsString::from("--port"), + OsString::from(format!("{}", port)), + ], + &[( + "RUST_LOG", + "server_core=debug,cli_core=debug,actix_net=info", + )], ); let start = Instant::now(); let mut found = false; - while start.elapsed() < Duration::from_secs( 20 ) { - thread::sleep( Duration::from_millis( 100 ) ); - if let Some( response ) = attohttpc::get( &format!( "http://localhost:{}/list", port ) ).send().ok() { - assert_eq!( response.status(), attohttpc::StatusCode::OK ); - assert_eq!( *response.headers().get( attohttpc::header::CONTENT_TYPE ).unwrap(), "application/json" ); - let list: Vec< ResponseMetadata > = serde_json::from_str( &response.text().unwrap() ).unwrap(); + while start.elapsed() < Duration::from_secs(20) { + thread::sleep(Duration::from_millis(100)); + if let Some(response) = attohttpc::get(&format!("http://localhost:{}/list", port)) + .send() + .ok() + { + assert_eq!(response.status(), attohttpc::StatusCode::OK); + assert_eq!( + *response + .headers() + .get(attohttpc::header::CONTENT_TYPE) + .unwrap(), + "application/json" + ); + let list: Vec = + serde_json::from_str(&response.text().unwrap()).unwrap(); if !list.is_empty() { - assert_eq!( list[ 0 ].executable.split( "/" ).last().unwrap(), name ); + assert_eq!(list[0].executable.split("/").last().unwrap(), name); found = true; break; } } } - assert!( found ); - - let response = attohttpc::get( &format!( "http://localhost:{}/data/last/allocations", port ) ).send().unwrap(); - assert_eq!( response.status(), attohttpc::StatusCode::OK ); - assert_eq!( *response.headers().get( attohttpc::header::CONTENT_TYPE ).unwrap(), "application/json" ); - let response: ResponseAllocations = serde_json::from_str( &response.text().unwrap() ).unwrap(); - - let groups = attohttpc::get( &format!( "http://localhost:{}/data/last/allocation_groups", port ) ).send().unwrap(); - assert_eq!( groups.status(), attohttpc::StatusCode::OK ); - assert_eq!( *groups.headers().get( attohttpc::header::CONTENT_TYPE ).unwrap(), "application/json" ); - let groups: ResponseAllocationGroups = serde_json::from_str( &groups.text().unwrap() ).unwrap(); + assert!(found); - let maps = attohttpc::get( &format!( "http://localhost:{}/data/last/maps?with_regions=true&with_usage_history=true", port ) ).send().unwrap(); - assert_eq!( maps.status(), attohttpc::StatusCode::OK ); - assert_eq!( *maps.headers().get( attohttpc::header::CONTENT_TYPE ).unwrap(), "application/json" ); - let response_maps: ResponseMaps = serde_json::from_str( &maps.text().unwrap() ).unwrap(); + let response = attohttpc::get(&format!("http://localhost:{}/data/last/allocations", port)) + .send() + .unwrap(); + assert_eq!(response.status(), attohttpc::StatusCode::OK); + assert_eq!( + *response + .headers() + .get(attohttpc::header::CONTENT_TYPE) + .unwrap(), + "application/json" + ); + let response: ResponseAllocations = serde_json::from_str(&response.text().unwrap()).unwrap(); + + let groups = attohttpc::get(&format!( + "http://localhost:{}/data/last/allocation_groups", + port + )) + .send() + .unwrap(); + assert_eq!(groups.status(), attohttpc::StatusCode::OK); + assert_eq!( + *groups + .headers() + .get(attohttpc::header::CONTENT_TYPE) + .unwrap(), + "application/json" + ); + let groups: ResponseAllocationGroups = serde_json::from_str(&groups.text().unwrap()).unwrap(); + + let maps = attohttpc::get(&format!( + "http://localhost:{}/data/last/maps?with_regions=true&with_usage_history=true", + port + )) + .send() + .unwrap(); + assert_eq!(maps.status(), attohttpc::StatusCode::OK); + assert_eq!( + *maps.headers().get(attohttpc::header::CONTENT_TYPE).unwrap(), + "application/json" + ); + let response_maps: ResponseMaps = serde_json::from_str(&maps.text().unwrap()).unwrap(); - Analysis { response, groups, response_maps } + Analysis { + response, + groups, + response_maps, + } } -fn get_basename( path: &str ) -> &str { - let index_slash = path.rfind( "/" ).map( |index| index + 1 ).unwrap_or( 0 ); - let index_dot = path.rfind( "." ).unwrap(); - &path[ index_slash..index_dot ] +fn get_basename(path: &str) -> &str { + let index_slash = path.rfind("/").map(|index| index + 1).unwrap_or(0); + let index_dot = path.rfind(".").unwrap(); + &path[index_slash..index_dot] } -fn compile_with_cargo( source: &str ) -> PathBuf { - let source_path = repository_root().join( "integration-tests" ).join( "test-programs" ).join( source ); - let args: Vec< &str > = vec![ "build" ]; - let target_dir = build_root().join( "test-programs" ); +fn compile_with_cargo(source: &str) -> PathBuf { + let source_path = repository_root() + .join("integration-tests") + .join("test-programs") + .join(source); + let args: Vec<&str> = vec!["build"]; + let target_dir = build_root().join("test-programs"); run_with_timeout( &source_path, "cargo", &args, - &[ - ("CARGO_TARGET_DIR", target_dir.clone()) - ], - Duration::from_secs( 180 ) - ).assert_success(); + &[("CARGO_TARGET_DIR", target_dir.clone())], + Duration::from_secs(180), + ) + .assert_success(); - target_dir.join( "debug" ) + target_dir.join("debug") } -fn compile_with_rustc( source: &str ) -> PathBuf { +fn compile_with_rustc(source: &str) -> PathBuf { let cwd = workdir(); - let source_path = repository_root().join( "integration-tests" ).join( "test-programs" ).join( source ); - let output_path = PathBuf::from( get_basename( source ) ); + let source_path = repository_root() + .join("integration-tests") + .join("test-programs") + .join(source); + let output_path = PathBuf::from(get_basename(source)); run( &cwd, "rustc", &[ source_path.as_os_str(), - std::ffi::OsStr::new( "-o" ), - output_path.as_os_str() + std::ffi::OsStr::new("-o"), + output_path.as_os_str(), ], - EMPTY_ENV - ).assert_success(); + EMPTY_ENV, + ) + .assert_success(); output_path } -fn compile_with_flags( source: &str, extra_flags: &[&str] ) { +fn compile_with_flags(source: &str, extra_flags: &[&str]) { let cwd = workdir(); - let basename = get_basename( source ); - let source_path = repository_root().join( "integration-tests" ).join( "test-programs" ).join( source ); + let basename = get_basename(source); + let source_path = repository_root() + .join("integration-tests") + .join("test-programs") + .join(source); let source_path = source_path.into_os_string().into_string().unwrap(); - let mut args: Vec< &str > = Vec::new(); - if !source.ends_with( ".c" ) { - args.push( "-std=c++11" ); + let mut args: Vec<&str> = Vec::new(); + if !source.ends_with(".c") { + args.push("-std=c++11"); } - args.extend( &[ + args.extend(&[ "-fasynchronous-unwind-tables", "-Wno-unused-result", "-O0", @@ -473,85 +549,90 @@ fn compile_with_flags( source: &str, extra_flags: &[&str] ) { "-ggdb3", &source_path, "-o", - basename + basename, ]); - args.extend( extra_flags ); - if source.ends_with( ".c" ) { - run( - &cwd, - compiler_cc(), - &args, - EMPTY_ENV - ).assert_success(); + args.extend(extra_flags); + if source.ends_with(".c") { + run(&cwd, compiler_cc(), &args, EMPTY_ENV).assert_success(); } else { - run( - &cwd, - compiler_cxx(), - &args, - EMPTY_ENV - ).assert_success(); + run(&cwd, compiler_cxx(), &args, EMPTY_ENV).assert_success(); } } -fn compile( source: &str ) { - compile_with_flags( source, &[] ); +fn compile(source: &str) { + compile_with_flags(source, &[]); } fn map_to_target( - executable: impl AsRef< OsStr >, - args: &[impl AsRef< OsStr >], - envs: &[(impl AsRef< OsStr >, impl AsRef< OsStr >)] -) -> (OsString, Vec< OsString >, Vec< (OsString, OsString) >) { + executable: impl AsRef, + args: &[impl AsRef], + envs: &[(impl AsRef, impl AsRef)], +) -> (OsString, Vec, Vec<(OsString, OsString)>) { let mut executable = executable.as_ref().to_owned(); - let mut args: Vec< OsString > = - args.iter().map( |arg| arg.as_ref().to_owned() ).collect(); - let mut envs: Vec< (OsString, OsString) > = - envs.iter().map( |(key, value)| (key.as_ref().to_owned(), value.as_ref().to_owned()) ).collect(); - - if let Some( runner ) = std::env::var_os( "MEMORY_PROFILER_TEST_RUNNER" ) { - args = std::iter::once( executable ).chain( args.into_iter() ).collect(); + let mut args: Vec = args.iter().map(|arg| arg.as_ref().to_owned()).collect(); + let mut envs: Vec<(OsString, OsString)> = envs + .iter() + .map(|(key, value)| (key.as_ref().to_owned(), value.as_ref().to_owned())) + .collect(); + + if let Some(runner) = std::env::var_os("MEMORY_PROFILER_TEST_RUNNER") { + args = std::iter::once(executable) + .chain(args.into_iter()) + .collect(); executable = runner; - if let Some( index ) = envs.iter().position( |&(ref key, _)| key == "LD_PRELOAD" ) { - let (_, value) = envs.remove( index ); - envs.push( ("TARGET_LD_PRELOAD".into(), value) ); + if let Some(index) = envs.iter().position(|&(ref key, _)| key == "LD_PRELOAD") { + let (_, value) = envs.remove(index); + envs.push(("TARGET_LD_PRELOAD".into(), value)); } } (executable, args, envs) } -pub fn run_on_target< C, E, S, P, Q >( cwd: C, executable: E, args: &[S], envs: &[(P, Q)] ) -> CommandResult - where C: AsRef< Path >, - E: AsRef< OsStr >, - S: AsRef< OsStr >, - P: AsRef< OsStr >, - Q: AsRef< OsStr > +pub fn run_on_target( + cwd: C, + executable: E, + args: &[S], + envs: &[(P, Q)], +) -> CommandResult +where + C: AsRef, + E: AsRef, + S: AsRef, + P: AsRef, + Q: AsRef, { - let (executable, args, envs) = map_to_target( executable, args, envs ); - run( cwd, executable, &args, &envs ) -} - -pub fn run_in_the_background_on_target< C, E, S, P, Q >( cwd: C, executable: E, args: &[S], envs: &[(P, Q)] ) -> ChildHandle - where C: AsRef< Path >, - E: AsRef< OsStr >, - S: AsRef< OsStr >, - P: AsRef< OsStr >, - Q: AsRef< OsStr > + let (executable, args, envs) = map_to_target(executable, args, envs); + run(cwd, executable, &args, &envs) +} + +pub fn run_in_the_background_on_target( + cwd: C, + executable: E, + args: &[S], + envs: &[(P, Q)], +) -> ChildHandle +where + C: AsRef, + E: AsRef, + S: AsRef, + P: AsRef, + Q: AsRef, { - let (executable, args, envs) = map_to_target( executable, args, envs ); - run_in_the_background( cwd, executable, &args, &envs ) + let (executable, args, envs) = map_to_target(executable, args, envs); + run_in_the_background(cwd, executable, &args, &envs) } fn get_log_level() -> OsString { - std::env::var_os( "MEMORY_PROFILER_LOG" ).unwrap_or_else( || "debug".into() ) + std::env::var_os("MEMORY_PROFILER_LOG").unwrap_or_else(|| "debug".into()) } #[test] fn test_basic() { let cwd = workdir(); - compile( "basic.c" ); + compile("basic.c"); run_on_target( &cwd, @@ -560,18 +641,22 @@ fn test_basic() { &[ ("LD_PRELOAD", preload_path().into_os_string()), ("MEMORY_PROFILER_LOG", get_log_level()), - ("MEMORY_PROFILER_OUTPUT", "memory-profiling-basic.dat".into()) - ] - ).assert_success(); + ( + "MEMORY_PROFILER_OUTPUT", + "memory-profiling-basic.dat".into(), + ), + ], + ) + .assert_success(); - check_allocations_basic_program( "basic", &cwd.join( "memory-profiling-basic.dat" ) ); + check_allocations_basic_program("basic", &cwd.join("memory-profiling-basic.dat")); } -fn test_mmap_impl( use_set_vma_anon_name: bool ) { +fn test_mmap_impl(use_set_vma_anon_name: bool) { let cwd = workdir(); static ONCE: std::sync::Once = std::sync::Once::new(); - ONCE.call_once( || compile( "mmap.c" ) ); + ONCE.call_once(|| compile("mmap.c")); let env_disable_pr_set_vma_anon_name; let output_filename; @@ -592,173 +677,197 @@ fn test_mmap_impl( use_set_vma_anon_name: bool ) { ("LD_PRELOAD", preload_path().into_os_string()), ("MEMORY_PROFILER_LOG", get_log_level()), ("MEMORY_PROFILER_OUTPUT", output_filename.into()), - ("MEMORY_PROFILER_DISABLE_PR_SET_VMA_ANON_NAME", env_disable_pr_set_vma_anon_name.into()) - ] - ).assert_success(); + ( + "MEMORY_PROFILER_DISABLE_PR_SET_VMA_ANON_NAME", + env_disable_pr_set_vma_anon_name.into(), + ), + ], + ) + .assert_success(); - let analysis = analyze( "mmap", cwd.join( output_filename ) ); + let analysis = analyze("mmap", cwd.join(output_filename)); let mut skipping = true; - let mut iter = analysis.response_maps.maps.into_iter().skip_while( |map| { - if map.size == 123 * 4096 { - skipping = false; - } - skipping - }).filter( |map| { - map.source.as_ref().map( |source| { - source.backtrace.iter().any( |frame| { - frame.source.as_ref().map( |source| { - source.ends_with( "mmap.c" ) - }).unwrap_or( false ) - }) - }).unwrap_or( false ) - }); + let mut iter = analysis + .response_maps + .maps + .into_iter() + .skip_while(|map| { + if map.size == 123 * 4096 { + skipping = false; + } + skipping + }) + .filter(|map| { + map.source + .as_ref() + .map(|source| { + source.backtrace.iter().any(|frame| { + frame + .source + .as_ref() + .map(|source| source.ends_with("mmap.c")) + .unwrap_or(false) + }) + }) + .unwrap_or(false) + }); // Leaked, never touched. let a0 = iter.next().unwrap(); - assert_eq!( a0.name, "[anon:mmap]" ); - assert_eq!( a0.regions.len(), 1 ); - assert_eq!( a0.size, 123 * 4096 ); - assert!( a0.source.is_some() ); - assert!( a0.deallocation.is_none() ); - assert!( a0.is_readable ); - assert!( a0.is_writable ); - assert!( !a0.is_executable ); - assert!( !a0.is_shared ); - assert_eq!( a0.regions[ 0 ].file_offset, 0 ); - assert_eq!( a0.regions[ 0 ].inode, 0 ); - assert_eq!( a0.regions[ 0 ].major, 0 ); - assert_eq!( a0.regions[ 0 ].minor, 0 ); - assert_eq!( a0.peak_rss, 0 ); - assert_eq!( a0.usage_history.len(), 1 ); - assert_eq!( a0.usage_history[ 0 ].address_space, 123 * 4096 ); - assert_eq!( a0.usage_history[ 0 ].rss, 0 ); + assert_eq!(a0.name, "[anon:mmap]"); + assert_eq!(a0.regions.len(), 1); + assert_eq!(a0.size, 123 * 4096); + assert!(a0.source.is_some()); + assert!(a0.deallocation.is_none()); + assert!(a0.is_readable); + assert!(a0.is_writable); + assert!(!a0.is_executable); + assert!(!a0.is_shared); + assert_eq!(a0.regions[0].file_offset, 0); + assert_eq!(a0.regions[0].inode, 0); + assert_eq!(a0.regions[0].major, 0); + assert_eq!(a0.regions[0].minor, 0); + assert_eq!(a0.peak_rss, 0); + assert_eq!(a0.usage_history.len(), 1); + assert_eq!(a0.usage_history[0].address_space, 123 * 4096); + assert_eq!(a0.usage_history[0].rss, 0); // Leaked, touched. let a1 = iter.next().unwrap(); - assert_eq!( a1.name, "[anon:mmap]" ); - assert_eq!( a1.regions.len(), 1 ); - assert_eq!( a1.size, 5 * 4096 ); - assert_eq!( a1.peak_rss, 2 * 4096 ); - assert!( !a1.source.is_none() ); - assert!( a1.deallocation.is_none() ); - assert_eq!( a1.usage_history.len(), 2 ); - assert_eq!( a1.usage_history[ 0 ].address_space, 5 * 4096 ); - assert_eq!( a1.usage_history[ 0 ].rss, 0 ); - assert_eq!( a1.usage_history[ 1 ].address_space, 5 * 4096 ); - assert_eq!( a1.usage_history[ 1 ].rss, 8192 ); - assert!( a1.usage_history[ 0 ].timestamp < a1.usage_history[ 1 ].timestamp ); + assert_eq!(a1.name, "[anon:mmap]"); + assert_eq!(a1.regions.len(), 1); + assert_eq!(a1.size, 5 * 4096); + assert_eq!(a1.peak_rss, 2 * 4096); + assert!(!a1.source.is_none()); + assert!(a1.deallocation.is_none()); + assert_eq!(a1.usage_history.len(), 2); + assert_eq!(a1.usage_history[0].address_space, 5 * 4096); + assert_eq!(a1.usage_history[0].rss, 0); + assert_eq!(a1.usage_history[1].address_space, 5 * 4096); + assert_eq!(a1.usage_history[1].rss, 8192); + assert!(a1.usage_history[0].timestamp < a1.usage_history[1].timestamp); // Fully deallocated. let a2 = iter.next().unwrap(); - assert_eq!( a2.name, "[anon:mmap]" ); - assert_eq!( a2.regions.len(), 1 ); - assert_eq!( a2.size, 6 * 4096 ); - assert!( !a2.source.is_none() ); - assert!( a2.deallocation.is_some() ); - assert_eq!( a2.usage_history.len(), 2 ); - assert_eq!( a2.usage_history[ 0 ].address_space, 6 * 4096 ); - assert_eq!( a2.usage_history[ 0 ].rss, 0 ); - assert_eq!( a2.usage_history[ 1 ].address_space, 0 ); - assert_eq!( a2.usage_history[ 1 ].rss, 0 ); - assert!( a2.usage_history[ 0 ].timestamp < a2.usage_history[ 1 ].timestamp ); + assert_eq!(a2.name, "[anon:mmap]"); + assert_eq!(a2.regions.len(), 1); + assert_eq!(a2.size, 6 * 4096); + assert!(!a2.source.is_none()); + assert!(a2.deallocation.is_some()); + assert_eq!(a2.usage_history.len(), 2); + assert_eq!(a2.usage_history[0].address_space, 6 * 4096); + assert_eq!(a2.usage_history[0].rss, 0); + assert_eq!(a2.usage_history[1].address_space, 0); + assert_eq!(a2.usage_history[1].rss, 0); + assert!(a2.usage_history[0].timestamp < a2.usage_history[1].timestamp); // Partially deallocated at the start. let a3 = iter.next().unwrap(); - assert_eq!( a3.name, "[anon:mmap]" ); - assert_eq!( a3.regions.len(), 2 ); + assert_eq!(a3.name, "[anon:mmap]"); + assert_eq!(a3.regions.len(), 2); - assert_eq!( a3.regions[ 0 ].size, 7 * 4096 ); // Original. - assert!( a3.regions[ 0 ].deallocation.is_some() ); + assert_eq!(a3.regions[0].size, 7 * 4096); // Original. + assert!(a3.regions[0].deallocation.is_some()); - assert_eq!( a3.regions[ 1 ].size, 1 * 4096 ); // After it was cut down. (The end part of the original.) - assert!( a3.regions[ 1 ].deallocation.is_none() ); + assert_eq!(a3.regions[1].size, 1 * 4096); // After it was cut down. (The end part of the original.) + assert!(a3.regions[1].deallocation.is_none()); - assert_ne!( a3.regions[ 0 ].timestamp, a3.regions[ 1 ].timestamp ); - assert_eq!( a3.regions[ 0 ].deallocation.as_ref().unwrap().timestamp, a3.regions[ 1 ].timestamp ); - assert_eq!( a3.regions[ 0 ].address + 6 * 4096, a3.regions[ 1 ].address ); + assert_ne!(a3.regions[0].timestamp, a3.regions[1].timestamp); + assert_eq!( + a3.regions[0].deallocation.as_ref().unwrap().timestamp, + a3.regions[1].timestamp + ); + assert_eq!(a3.regions[0].address + 6 * 4096, a3.regions[1].address); // Partially deallocated at the end. let a4 = iter.next().unwrap(); - assert_eq!( a4.name, "[anon:mmap]" ); - assert_eq!( a4.regions.len(), 2 ); + assert_eq!(a4.name, "[anon:mmap]"); + assert_eq!(a4.regions.len(), 2); - assert_eq!( a4.regions[ 0 ].size, 7 * 4096 ); // Original. - assert!( a4.regions[ 0 ].deallocation.is_some() ); + assert_eq!(a4.regions[0].size, 7 * 4096); // Original. + assert!(a4.regions[0].deallocation.is_some()); - assert_eq!( a4.regions[ 1 ].size, 1 * 4096 ); // After it was cut down. (The start part of the original.) - assert!( a4.regions[ 1 ].deallocation.is_none() ); + assert_eq!(a4.regions[1].size, 1 * 4096); // After it was cut down. (The start part of the original.) + assert!(a4.regions[1].deallocation.is_none()); - assert_ne!( a4.regions[ 0 ].timestamp, a4.regions[ 1 ].timestamp ); - assert_eq!( a4.regions[ 0 ].address, a4.regions[ 0 ].address ); + assert_ne!(a4.regions[0].timestamp, a4.regions[1].timestamp); + assert_eq!(a4.regions[0].address, a4.regions[0].address); // Partially deallocated in the middle. let a5 = iter.next().unwrap(); - assert_eq!( a5.name, "[anon:mmap]" ); - assert_eq!( a5.regions.len(), 3 ); + assert_eq!(a5.name, "[anon:mmap]"); + assert_eq!(a5.regions.len(), 3); - assert_eq!( a5.regions[ 0 ].size, 7 * 4096 ); // Original. - assert!( a5.regions[ 0 ].deallocation.is_some() ); + assert_eq!(a5.regions[0].size, 7 * 4096); // Original. + assert!(a5.regions[0].deallocation.is_some()); - assert_eq!( a5.regions[ 1 ].size, 3 * 4096 ); // Cut down (1). - assert_eq!( a5.regions[ 0 ].address, a5.regions[ 1 ].address ); - assert!( a5.regions[ 1 ].deallocation.is_none() ); + assert_eq!(a5.regions[1].size, 3 * 4096); // Cut down (1). + assert_eq!(a5.regions[0].address, a5.regions[1].address); + assert!(a5.regions[1].deallocation.is_none()); - assert_eq!( a5.regions[ 2 ].size, 3 * 4096 ); // Cut down (2). - assert_eq!( a5.regions[ 0 ].address + 4 * 4096, a5.regions[ 2 ].address ); - assert!( a5.regions[ 2 ].deallocation.is_none() ); + assert_eq!(a5.regions[2].size, 3 * 4096); // Cut down (2). + assert_eq!(a5.regions[0].address + 4 * 4096, a5.regions[2].address); + assert!(a5.regions[2].deallocation.is_none()); // Partially deallocated with another mmap. let a6 = iter.next().unwrap(); - assert_eq!( a6.name, "[anon:mmap]" ); - assert_eq!( a6.regions.len(), 2 ); + assert_eq!(a6.name, "[anon:mmap]"); + assert_eq!(a6.regions.len(), 2); - assert_eq!( a6.regions[ 0 ].size, 7 * 4096 ); // Original. - assert!( a6.regions[ 0 ].deallocation.is_some() ); + assert_eq!(a6.regions[0].size, 7 * 4096); // Original. + assert!(a6.regions[0].deallocation.is_some()); - assert_eq!( a6.regions[ 1 ].size, 6 * 4096 ); // After it was cut down. - assert!( a6.regions[ 1 ].deallocation.is_none() ); + assert_eq!(a6.regions[1].size, 6 * 4096); // After it was cut down. + assert!(a6.regions[1].deallocation.is_none()); - assert_eq!( a6.regions[ 0 ].address, a6.regions[ 1 ].address ); + assert_eq!(a6.regions[0].address, a6.regions[1].address); // Another mmap which partially deallocated the previous map. let a7 = iter.next().unwrap(); - assert_eq!( a7.name, "[anon:mmap]" ); - assert_eq!( a7.regions.len(), 1 ); - assert_eq!( a7.regions[ 0 ].size, 4096 ); - assert_eq!( a6.regions[ 0 ].address + 6 * 4096, a7.regions[ 0 ].address ); - assert_eq!( Some( &a6.regions[ 0 ].deallocation.as_ref().unwrap().sources[ 0 ].source ), a7.source.as_ref() ); + assert_eq!(a7.name, "[anon:mmap]"); + assert_eq!(a7.regions.len(), 1); + assert_eq!(a7.regions[0].size, 4096); + assert_eq!(a6.regions[0].address + 6 * 4096, a7.regions[0].address); + assert_eq!( + Some(&a6.regions[0].deallocation.as_ref().unwrap().sources[0].source), + a7.source.as_ref() + ); } #[test] fn test_mmap_with_set_vma_anon_name() { - test_mmap_impl( true ); + test_mmap_impl(true); } #[test] fn test_mmap_without_set_vma_anon_name() { - test_mmap_impl( false ); + test_mmap_impl(false); } #[cfg(test)] -fn run_jemalloc_test( name: &str ) { - let cwd = compile_with_cargo( &format!( "jemalloc/{}", name ) ); +fn run_jemalloc_test(name: &str) { + let cwd = compile_with_cargo(&format!("jemalloc/{}", name)); run_on_target( &cwd, - &format!( "./{}", name ), + &format!("./{}", name), EMPTY_ARGS, &[ ("LD_PRELOAD", preload_path().into_os_string()), ("MEMORY_PROFILER_LOG", get_log_level()), - ("MEMORY_PROFILER_OUTPUT", format!( "memory-profiling-{}.dat", name ).into()) - ] - ).assert_success(); + ( + "MEMORY_PROFILER_OUTPUT", + format!("memory-profiling-{}.dat", name).into(), + ), + ], + ) + .assert_success(); - let analysis = analyze( name, cwd.join( format!( "memory-profiling-{}.dat", name ) ) ); - let mut iter = analysis.allocations_from_source( "main.rs" ).filter( |alloc| { - is_from_function_fuzzy( alloc, "run_test" ) - }); + let analysis = analyze(name, cwd.join(format!("memory-profiling-{}.dat", name))); + let mut iter = analysis + .allocations_from_source("main.rs") + .filter(|alloc| is_from_function_fuzzy(alloc, "run_test")); let a0 = iter.next().unwrap(); // (jemalloc) malloc, leaked let a1 = iter.next().unwrap(); // (jemalloc) malloc, freed @@ -768,59 +877,59 @@ fn run_jemalloc_test( name: &str ) { let a5 = iter.next().unwrap(); // (libc) malloc, freed through realloc let a6 = iter.next().unwrap(); // (libc) realloc, freed - assert!( a0.deallocation.is_none() ); - assert!( a1.deallocation.is_some() ); - assert!( a2.deallocation.is_some() ); - assert!( a3.deallocation.is_none() ); - assert!( a4.deallocation.is_none() ); - assert!( a5.deallocation.is_some() ); - assert!( a6.deallocation.is_some() ); - - assert!( a0.size < a1.size ); - assert!( a1.size < a2.size ); - assert!( a2.size < a3.size ); - assert!( a3.size < a4.size ); - assert_eq!( a5.size, 200 ); - assert_eq!( a6.size, 400 ); - - assert_eq!( a0.thread, a1.thread ); - assert_eq!( a1.thread, a2.thread ); - assert_eq!( a2.thread, a3.thread ); - assert_eq!( a3.thread, a4.thread ); - assert_eq!( a4.thread, a5.thread ); - assert_eq!( a5.thread, a6.thread ); - - assert_eq!( a0.chain_length, 1 ); - assert_eq!( a1.chain_length, 1 ); - assert_eq!( a2.chain_length, 2 ); - assert_eq!( a3.chain_length, 2 ); - assert_eq!( a4.chain_length, 1 ); - assert_eq!( a5.chain_length, 2 ); - assert_eq!( a6.chain_length, 2 ); - - assert_eq!( iter.next(), None ); + assert!(a0.deallocation.is_none()); + assert!(a1.deallocation.is_some()); + assert!(a2.deallocation.is_some()); + assert!(a3.deallocation.is_none()); + assert!(a4.deallocation.is_none()); + assert!(a5.deallocation.is_some()); + assert!(a6.deallocation.is_some()); + + assert!(a0.size < a1.size); + assert!(a1.size < a2.size); + assert!(a2.size < a3.size); + assert!(a3.size < a4.size); + assert_eq!(a5.size, 200); + assert_eq!(a6.size, 400); + + assert_eq!(a0.thread, a1.thread); + assert_eq!(a1.thread, a2.thread); + assert_eq!(a2.thread, a3.thread); + assert_eq!(a3.thread, a4.thread); + assert_eq!(a4.thread, a5.thread); + assert_eq!(a5.thread, a6.thread); + + assert_eq!(a0.chain_length, 1); + assert_eq!(a1.chain_length, 1); + assert_eq!(a2.chain_length, 2); + assert_eq!(a3.chain_length, 2); + assert_eq!(a4.chain_length, 1); + assert_eq!(a5.chain_length, 2); + assert_eq!(a6.chain_length, 2); + + assert_eq!(iter.next(), None); } #[test] fn test_jemalloc_v03_prefixed() { - run_jemalloc_test( "jemalloc-v03" ); + run_jemalloc_test("jemalloc-v03"); } #[test] fn test_jemalloc_v05_prefixed() { - run_jemalloc_test( "jemalloc-v05" ); + run_jemalloc_test("jemalloc-v05"); } #[test] fn test_jemalloc_v05_unprefixed() { - run_jemalloc_test( "jemalloc-v05-unprefixed" ); + run_jemalloc_test("jemalloc-v05-unprefixed"); } #[test] fn test_alloc_in_tls() { let cwd = workdir(); - compile( "alloc-in-tls.cpp" ); + compile("alloc-in-tls.cpp"); run_on_target( &cwd, @@ -829,74 +938,85 @@ fn test_alloc_in_tls() { &[ ("LD_PRELOAD", preload_path().into_os_string()), ("MEMORY_PROFILER_LOG", get_log_level()), - ("MEMORY_PROFILER_OUTPUT", "memory-profiling-alloc-in-tls.dat".into()) - ] - ).assert_success(); + ( + "MEMORY_PROFILER_OUTPUT", + "memory-profiling-alloc-in-tls.dat".into(), + ), + ], + ) + .assert_success(); - assert_file_exists( cwd.join( "memory-profiling-alloc-in-tls.dat" ) ); + assert_file_exists(cwd.join("memory-profiling-alloc-in-tls.dat")); } -fn test_start_stop_generic( kind: &str ) { +fn test_start_stop_generic(kind: &str) { let cwd = workdir(); - let output = format!( "start-stop_{}", kind ); - let define = format!( "VARIANT_{}", kind.to_uppercase() ); - compile_with_flags( "start-stop.c", &[ "-o", &output, "-D", &define, "-fPIC" ] ); + let output = format!("start-stop_{}", kind); + let define = format!("VARIANT_{}", kind.to_uppercase()); + compile_with_flags("start-stop.c", &["-o", &output, "-D", &define, "-fPIC"]); run_on_target( &cwd, - &format!( "./{}", output ), + &format!("./{}", output), EMPTY_ARGS, &[ ("LD_PRELOAD", preload_path().into_os_string()), ("MEMORY_PROFILER_LOG", get_log_level()), - ("MEMORY_PROFILER_OUTPUT", format!( "start-stop_{}.dat", kind ).into()), - ("MEMORY_PROFILER_DISABLE_BY_DEFAULT", "1".into()) - ] - ).assert_success(); + ( + "MEMORY_PROFILER_OUTPUT", + format!("start-stop_{}.dat", kind).into(), + ), + ("MEMORY_PROFILER_DISABLE_BY_DEFAULT", "1".into()), + ], + ) + .assert_success(); - let analysis = analyze( &format!( "start-stop_{}", kind ), cwd.join( format!( "start-stop_{}.dat", kind ) ) ); + let analysis = analyze( + &format!("start-stop_{}", kind), + cwd.join(format!("start-stop_{}.dat", kind)), + ); - let mut iter = analysis.allocations_from_source( "start-stop.c" ); + let mut iter = analysis.allocations_from_source("start-stop.c"); let a0 = iter.next().unwrap(); let a1 = iter.next().unwrap(); let a2 = iter.next().unwrap(); let a3 = iter.next().unwrap(); let a4 = iter.next().unwrap(); - assert_eq!( a0.size, 10002 ); - assert_eq!( a1.size, 20002 ); - assert_eq!( a2.size, 10003 ); - assert_eq!( a3.size, 10004 ); - assert_eq!( a4.size, 20003 ); + assert_eq!(a0.size, 10002); + assert_eq!(a1.size, 20002); + assert_eq!(a2.size, 10003); + assert_eq!(a3.size, 10004); + assert_eq!(a4.size, 20003); - assert_eq!( a0.thread, a2.thread ); - assert_ne!( a0.thread, a1.thread ); - assert_ne!( a3.thread, a4.thread ); + assert_eq!(a0.thread, a2.thread); + assert_ne!(a0.thread, a1.thread); + assert_ne!(a3.thread, a4.thread); - assert!( a0.deallocation.is_some() ); - assert!( a1.deallocation.is_none() ); - assert!( a2.deallocation.is_none() ); - assert!( a3.deallocation.is_none() ); - assert!( a4.deallocation.is_none() ); + assert!(a0.deallocation.is_some()); + assert!(a1.deallocation.is_none()); + assert!(a2.deallocation.is_none()); + assert!(a3.deallocation.is_none()); + assert!(a4.deallocation.is_none()); - assert_eq!( iter.next(), None ); + assert_eq!(iter.next(), None); } #[test] fn test_start_stop_sigusr1() { - test_start_stop_generic( "sigusr1" ); + test_start_stop_generic("sigusr1"); } #[test] fn test_start_stop_api() { - test_start_stop_generic( "api" ); + test_start_stop_generic("api"); } #[test] fn test_fork() { let cwd = workdir(); - compile( "fork.c" ); + compile("fork.c"); run_on_target( &cwd, @@ -905,18 +1025,18 @@ fn test_fork() { &[ ("LD_PRELOAD", preload_path().into_os_string()), ("MEMORY_PROFILER_LOG", get_log_level()), - ("MEMORY_PROFILER_OUTPUT", "fork_%n.dat".into()) - ] - ).assert_success(); + ("MEMORY_PROFILER_OUTPUT", "fork_%n.dat".into()), + ], + ) + .assert_success(); - assert_file_exists( cwd.join( "fork_0.dat" ) ); - assert_file_missing( cwd.join( "fork_1.dat" ) ); + assert_file_exists(cwd.join("fork_0.dat")); + assert_file_missing(cwd.join("fork_1.dat")); - let analysis = analyze( "fork", cwd.join( "fork_0.dat" ) ); + let analysis = analyze("fork", cwd.join("fork_0.dat")); - let mut iter = analysis.allocations_from_source( "fork.c" ).filter( |alloc| { - !is_from_function( alloc, "allocate_dtv" ) && - !is_from_function( alloc, "_dl_allocate_tls" ) + let mut iter = analysis.allocations_from_source("fork.c").filter(|alloc| { + !is_from_function(alloc, "allocate_dtv") && !is_from_function(alloc, "_dl_allocate_tls") }); let a0 = iter.next().unwrap(); let a1 = iter.next().unwrap(); @@ -924,25 +1044,25 @@ fn test_fork() { let a3 = iter.next().unwrap(); let a4 = iter.next().unwrap(); - assert_eq!( a0.size, 10001 ); - assert_eq!( a1.size, 20001 ); - assert_eq!( a2.size, 10002 ); - assert_eq!( a3.size, 20002 ); - assert_eq!( a4.size, 10003 ); + assert_eq!(a0.size, 10001); + assert_eq!(a1.size, 20001); + assert_eq!(a2.size, 10002); + assert_eq!(a3.size, 20002); + assert_eq!(a4.size, 10003); - assert_eq!( a0.thread, a2.thread ); - assert_eq!( a2.thread, a4.thread ); - assert_eq!( a1.thread, a3.thread ); - assert_ne!( a0.thread, a1.thread ); + assert_eq!(a0.thread, a2.thread); + assert_eq!(a2.thread, a4.thread); + assert_eq!(a1.thread, a3.thread); + assert_ne!(a0.thread, a1.thread); - assert_eq!( iter.next(), None ); + assert_eq!(iter.next(), None); } #[test] fn test_normal_exit() { let cwd = workdir(); - compile( "exit_1.c" ); + compile("exit_1.c"); run_on_target( &cwd, @@ -951,23 +1071,24 @@ fn test_normal_exit() { &[ ("LD_PRELOAD", preload_path().into_os_string()), ("MEMORY_PROFILER_LOG", get_log_level()), - ("MEMORY_PROFILER_OUTPUT", "exit_1.dat".into()) - ] - ).assert_success(); + ("MEMORY_PROFILER_OUTPUT", "exit_1.dat".into()), + ], + ) + .assert_success(); - let analysis = analyze( "exit_1", cwd.join( "exit_1.dat" ) ); + let analysis = analyze("exit_1", cwd.join("exit_1.dat")); - let mut iter = analysis.allocations_from_source( "exit_1.c" ); + let mut iter = analysis.allocations_from_source("exit_1.c"); let a0 = iter.next().unwrap(); - assert_eq!( a0.size, 11001 ); - assert_eq!( iter.next(), None ); + assert_eq!(a0.size, 11001); + assert_eq!(iter.next(), None); } #[test] fn test_immediate_exit_unistd() { let cwd = workdir(); - compile( "exit_2.c" ); + compile("exit_2.c"); run_on_target( &cwd, @@ -976,23 +1097,24 @@ fn test_immediate_exit_unistd() { &[ ("LD_PRELOAD", preload_path().into_os_string()), ("MEMORY_PROFILER_LOG", get_log_level()), - ("MEMORY_PROFILER_OUTPUT", "exit_2.dat".into()) - ] - ).assert_success(); + ("MEMORY_PROFILER_OUTPUT", "exit_2.dat".into()), + ], + ) + .assert_success(); - let analysis = analyze( "exit_2", cwd.join( "exit_2.dat" ) ); + let analysis = analyze("exit_2", cwd.join("exit_2.dat")); - let mut iter = analysis.allocations_from_source( "exit_2.c" ); + let mut iter = analysis.allocations_from_source("exit_2.c"); let a0 = iter.next().unwrap(); - assert_eq!( a0.size, 12001 ); - assert_eq!( iter.next(), None ); + assert_eq!(a0.size, 12001); + assert_eq!(iter.next(), None); } #[test] fn test_immediate_exit_stdlib() { let cwd = workdir(); - compile( "exit_3.c" ); + compile("exit_3.c"); run_on_target( &cwd, @@ -1001,50 +1123,57 @@ fn test_immediate_exit_stdlib() { &[ ("LD_PRELOAD", preload_path().into_os_string()), ("MEMORY_PROFILER_LOG", get_log_level()), - ("MEMORY_PROFILER_OUTPUT", "exit_3.dat".into()) - ] - ).assert_success(); + ("MEMORY_PROFILER_OUTPUT", "exit_3.dat".into()), + ], + ) + .assert_success(); - let analysis = analyze( "exit_3", cwd.join( "exit_3.dat" ) ); + let analysis = analyze("exit_3", cwd.join("exit_3.dat")); - let mut iter = analysis.allocations_from_source( "exit_3.c" ); + let mut iter = analysis.allocations_from_source("exit_3.c"); let a0 = iter.next().unwrap(); - assert_eq!( a0.size, 13001 ); - assert_eq!( iter.next(), None ); + assert_eq!(a0.size, 13001); + assert_eq!(iter.next(), None); } -struct GatherTestHandle< 'a > { +struct GatherTestHandle<'a> { pid: u32, - is_graceful: &'a mut bool + is_graceful: &'a mut bool, } -impl< 'a > GatherTestHandle< 'a > { - fn kill( self ) { +impl<'a> GatherTestHandle<'a> { + fn kill(self) { *self.is_graceful = false; - unsafe { libc::kill( self.pid as _, libc::SIGUSR2 ); } + unsafe { + libc::kill(self.pid as _, libc::SIGUSR2); + } } - fn early_return( self ) { - unsafe { libc::kill( self.pid as _, libc::SIGINT ); } + fn early_return(self) { + unsafe { + libc::kill(self.pid as _, libc::SIGINT); + } } - fn next( &self ) { - unsafe { libc::kill( self.pid as _, libc::SIGUSR1 ); } + fn next(&self) { + unsafe { + libc::kill(self.pid as _, libc::SIGUSR1); + } } - fn sleep( &self ) { - thread::sleep( Duration::from_millis( 1000 ) ); + fn sleep(&self) { + thread::sleep(Duration::from_millis(1000)); } } -fn test_gather_generic( expected_allocations: usize, callback: impl FnOnce( GatherTestHandle ) ) { +fn test_gather_generic(expected_allocations: usize, callback: impl FnOnce(GatherTestHandle)) { let cwd = workdir(); static ONCE: std::sync::Once = std::sync::Once::new(); - ONCE.call_once( || compile( "gather.c" ) ); + ONCE.call_once(|| compile("gather.c")); - static PORT: AtomicUsize = AtomicUsize::new( 8100 ); - let port = PORT.fetch_add( 1, Ordering::SeqCst ); + static PORT: AtomicUsize = AtomicUsize::new(8100); + let port = PORT.fetch_add(1, Ordering::SeqCst); let child = run_in_the_background_on_target( &cwd, @@ -1053,51 +1182,63 @@ fn test_gather_generic( expected_allocations: usize, callback: impl FnOnce( Gath &[ ("LD_PRELOAD", preload_path().into_os_string()), ("MEMORY_PROFILER_LOG", get_log_level()), - ("MEMORY_PROFILER_OUTPUT", format!( "gather_{}.dat", port ).into()), + ( + "MEMORY_PROFILER_OUTPUT", + format!("gather_{}.dat", port).into(), + ), ("MEMORY_PROFILER_REGISTER_SIGUSR1", "0".into()), ("MEMORY_PROFILER_REGISTER_SIGUSR2", "0".into()), ("MEMORY_PROFILER_ENABLE_SERVER", "1".into()), - ("MEMORY_PROFILER_BASE_SERVER_PORT", format!( "{}", port ).into()) - ] + ( + "MEMORY_PROFILER_BASE_SERVER_PORT", + format!("{}", port).into(), + ), + ], ); let timestamp = Instant::now(); let mut found = false; - while timestamp.elapsed() < Duration::from_secs( 30 ) { - if std::net::TcpStream::connect( format!( "127.0.0.1:{}", port ) ).is_ok() { + while timestamp.elapsed() < Duration::from_secs(30) { + if std::net::TcpStream::connect(format!("127.0.0.1:{}", port)).is_ok() { found = true; break; } - thread::sleep( Duration::from_millis( 100 ) ); + thread::sleep(Duration::from_millis(100)); } if !found { - panic!( "Couldn't connect to the embedded server" ); + panic!("Couldn't connect to the embedded server"); } - let tmp_path = cwd.join( "tmp" ).join( format!( "test-gather-{}", port ) ); + let tmp_path = cwd.join("tmp").join(format!("test-gather-{}", port)); if tmp_path.exists() { - std::fs::remove_dir_all( &tmp_path ).unwrap(); + std::fs::remove_dir_all(&tmp_path).unwrap(); } - std::fs::create_dir_all( &tmp_path ).unwrap(); + std::fs::create_dir_all(&tmp_path).unwrap(); let gather = run_in_the_background( &tmp_path, cli_path(), - &[OsString::from( "gather" ), OsString::from( format!( "127.0.0.1:{}", port ) )], - &[("RUST_LOG", "server_core=debug,cli_core=debug,actix_net=info")] + &[ + OsString::from("gather"), + OsString::from(format!("127.0.0.1:{}", port)), + ], + &[( + "RUST_LOG", + "server_core=debug,cli_core=debug,actix_net=info", + )], ); - thread::sleep( Duration::from_millis( 1000 ) ); + thread::sleep(Duration::from_millis(1000)); let mut is_graceful = true; let handle = GatherTestHandle { pid: child.pid(), - is_graceful: &mut is_graceful + is_graceful: &mut is_graceful, }; - callback( handle ); + callback(handle); if is_graceful { child.wait().assert_success(); @@ -1107,34 +1248,40 @@ fn test_gather_generic( expected_allocations: usize, callback: impl FnOnce( Gath gather.wait().assert_success(); - let outputs = dir_entries( tmp_path ).unwrap(); - assert_eq!( outputs.len(), 1, "Unexpected outputs: {:?}", outputs ); + let outputs = dir_entries(tmp_path).unwrap(); + assert_eq!(outputs.len(), 1, "Unexpected outputs: {:?}", outputs); - let analysis = analyze( "gather", outputs.into_iter().next().unwrap() ); - let mut iter = analysis.allocations_from_source( "gather.c" ); + let analysis = analyze("gather", outputs.into_iter().next().unwrap()); + let mut iter = analysis.allocations_from_source("gather.c"); - assert!( expected_allocations >= 1 && expected_allocations <= 3 ); + assert!(expected_allocations >= 1 && expected_allocations <= 3); if expected_allocations >= 1 { - let a0 = iter.next().expect( "Expected at least one allocation; got none" ); - assert_eq!( a0.size, 10001 ); + let a0 = iter + .next() + .expect("Expected at least one allocation; got none"); + assert_eq!(a0.size, 10001); } if expected_allocations >= 2 { - let a1 = iter.next().expect( "Expected at least two allocations; got only one" ); - assert_eq!( a1.size, 10002 ); + let a1 = iter + .next() + .expect("Expected at least two allocations; got only one"); + assert_eq!(a1.size, 10002); } if expected_allocations >= 3 { - let a2 = iter.next().expect( "Expected at least three allocations; got only two" ); - assert_eq!( a2.size, 10003 ); + let a2 = iter + .next() + .expect("Expected at least three allocations; got only two"); + assert_eq!(a2.size, 10003); } - assert_eq!( iter.next(), None, "Too many allocations" ); + assert_eq!(iter.next(), None, "Too many allocations"); } #[test] fn test_gather_full_graceful() { - test_gather_generic( 3, |handle| { + test_gather_generic(3, |handle| { handle.next(); handle.sleep(); handle.next(); @@ -1144,14 +1291,14 @@ fn test_gather_full_graceful() { #[test] fn test_gather_initial_graceful() { - test_gather_generic( 1, |handle| { + test_gather_generic(1, |handle| { handle.early_return(); }); } #[test] fn test_gather_initial_killed() { - test_gather_generic( 1, |handle| { + test_gather_generic(1, |handle| { handle.kill(); }); } @@ -1159,7 +1306,7 @@ fn test_gather_initial_killed() { #[ignore] #[test] fn test_gather_partial_graceful() { - test_gather_generic( 2, |handle| { + test_gather_generic(2, |handle| { handle.next(); handle.early_return(); }); @@ -1167,7 +1314,7 @@ fn test_gather_partial_graceful() { #[test] fn test_gather_partial_killed() { - test_gather_generic( 1, |handle| { + test_gather_generic(1, |handle| { handle.next(); handle.sleep(); handle.kill(); @@ -1177,8 +1324,8 @@ fn test_gather_partial_killed() { #[test] fn test_dlopen() { let cwd = workdir(); - compile_with_flags( "dlopen.c", &[ "-ldl" ] ); - compile_with_flags( "dlopen_so.c", &[ "-shared", "-fPIC" ] ); + compile_with_flags("dlopen.c", &["-ldl"]); + compile_with_flags("dlopen_so.c", &["-shared", "-fPIC"]); run_on_target( &cwd, @@ -1187,23 +1334,28 @@ fn test_dlopen() { &[ ("LD_PRELOAD", preload_path().into_os_string()), ("MEMORY_PROFILER_LOG", get_log_level()), - ("MEMORY_PROFILER_OUTPUT", "dlopen.dat".into()) - ] - ).assert_success(); + ("MEMORY_PROFILER_OUTPUT", "dlopen.dat".into()), + ], + ) + .assert_success(); - let analysis = analyze( "dlopen", cwd.join( "dlopen.dat" ) ); - assert!( analysis.response.allocations.iter().any( |alloc| alloc.size == 123123 ) ); + let analysis = analyze("dlopen", cwd.join("dlopen.dat")); + assert!(analysis + .response + .allocations + .iter() + .any(|alloc| alloc.size == 123123)); - let mut iter = analysis.allocations_from_source( "dlopen_so.c" ); + let mut iter = analysis.allocations_from_source("dlopen_so.c"); let a0 = iter.next().unwrap(); - assert_eq!( a0.size, 123123 ); - assert_eq!( iter.next(), None ); + assert_eq!(a0.size, 123123); + assert_eq!(iter.next(), None); } #[test] fn test_throw() { let cwd = workdir(); - compile( "throw.cpp" ); + compile("throw.cpp"); run_on_target( &cwd, @@ -1212,51 +1364,56 @@ fn test_throw() { &[ ("LD_PRELOAD", preload_path().into_os_string()), ("MEMORY_PROFILER_LOG", get_log_level()), - ("MEMORY_PROFILER_OUTPUT", "throw.dat".into()) - ] - ).assert_success(); - - let analysis = analyze( "throw", cwd.join( "throw.dat" ) ); - let a0 = analysis.response.allocations.iter().find( |alloc| alloc.size == 123456 ).unwrap(); - let a1 = analysis.response.allocations.iter().find( |alloc| alloc.size == 123457 ).unwrap(); - let a2 = analysis.response.allocations.iter().find( |alloc| alloc.size == 123458 ).unwrap(); - let a3 = analysis.response.allocations.iter().find( |alloc| alloc.size == 123459 ).unwrap(); - - assert_allocation_backtrace( a0, &[ - "malloc", - "foobar_0", - "foobar_1", - "foobar_2", - "foobar_3", - "foobar_4", - "foobar_5", - "main" - ]); + ("MEMORY_PROFILER_OUTPUT", "throw.dat".into()), + ], + ) + .assert_success(); + + let analysis = analyze("throw", cwd.join("throw.dat")); + let a0 = analysis + .response + .allocations + .iter() + .find(|alloc| alloc.size == 123456) + .unwrap(); + let a1 = analysis + .response + .allocations + .iter() + .find(|alloc| alloc.size == 123457) + .unwrap(); + let a2 = analysis + .response + .allocations + .iter() + .find(|alloc| alloc.size == 123458) + .unwrap(); + let a3 = analysis + .response + .allocations + .iter() + .find(|alloc| alloc.size == 123459) + .unwrap(); + + assert_allocation_backtrace( + a0, + &[ + "malloc", "foobar_0", "foobar_1", "foobar_2", "foobar_3", "foobar_4", "foobar_5", + "main", + ], + ); - assert_allocation_backtrace( a1, &[ - "malloc", - "foobar_3", - "foobar_4", - "foobar_5", - "main" - ]); + assert_allocation_backtrace(a1, &["malloc", "foobar_3", "foobar_4", "foobar_5", "main"]); - assert_allocation_backtrace( a2, &[ - "malloc", - "foobar_5", - "main" - ]); + assert_allocation_backtrace(a2, &["malloc", "foobar_5", "main"]); - assert_allocation_backtrace( a3, &[ - "malloc", - "main" - ]); + assert_allocation_backtrace(a3, &["malloc", "main"]); } #[test] fn test_longjmp() { let cwd = workdir(); - compile( "longjmp.c" ); + compile("longjmp.c"); run_on_target( &cwd, @@ -1265,51 +1422,56 @@ fn test_longjmp() { &[ ("LD_PRELOAD", preload_path().into_os_string()), ("MEMORY_PROFILER_LOG", get_log_level()), - ("MEMORY_PROFILER_OUTPUT", "longjmp.dat".into()) - ] - ).assert_success(); - - let analysis = analyze( "longjmp", cwd.join( "longjmp.dat" ) ); - let a0 = analysis.response.allocations.iter().find( |alloc| alloc.size == 123456 ).unwrap(); - let a1 = analysis.response.allocations.iter().find( |alloc| alloc.size == 123457 ).unwrap(); - let a2 = analysis.response.allocations.iter().find( |alloc| alloc.size == 123458 ).unwrap(); - let a3 = analysis.response.allocations.iter().find( |alloc| alloc.size == 123459 ).unwrap(); - - assert_allocation_backtrace( a0, &[ - "malloc", - "foobar_0", - "foobar_1", - "foobar_2", - "foobar_3", - "foobar_4", - "foobar_5", - "main" - ]); + ("MEMORY_PROFILER_OUTPUT", "longjmp.dat".into()), + ], + ) + .assert_success(); + + let analysis = analyze("longjmp", cwd.join("longjmp.dat")); + let a0 = analysis + .response + .allocations + .iter() + .find(|alloc| alloc.size == 123456) + .unwrap(); + let a1 = analysis + .response + .allocations + .iter() + .find(|alloc| alloc.size == 123457) + .unwrap(); + let a2 = analysis + .response + .allocations + .iter() + .find(|alloc| alloc.size == 123458) + .unwrap(); + let a3 = analysis + .response + .allocations + .iter() + .find(|alloc| alloc.size == 123459) + .unwrap(); + + assert_allocation_backtrace( + a0, + &[ + "malloc", "foobar_0", "foobar_1", "foobar_2", "foobar_3", "foobar_4", "foobar_5", + "main", + ], + ); - assert_allocation_backtrace( a1, &[ - "malloc", - "foobar_3", - "foobar_4", - "foobar_5", - "main" - ]); + assert_allocation_backtrace(a1, &["malloc", "foobar_3", "foobar_4", "foobar_5", "main"]); - assert_allocation_backtrace( a2, &[ - "malloc", - "foobar_5", - "main" - ]); + assert_allocation_backtrace(a2, &["malloc", "foobar_5", "main"]); - assert_allocation_backtrace( a3, &[ - "malloc", - "main" - ]); + assert_allocation_backtrace(a3, &["malloc", "main"]); } #[test] fn test_backtrace() { let cwd = workdir(); - compile_with_flags( "backtrace.c", &[ "-rdynamic" ] ); + compile_with_flags("backtrace.c", &["-rdynamic"]); run_on_target( &cwd, @@ -1318,18 +1480,23 @@ fn test_backtrace() { &[ ("LD_PRELOAD", preload_path().into_os_string()), ("MEMORY_PROFILER_LOG", get_log_level()), - ("MEMORY_PROFILER_OUTPUT", "backtrace.dat".into()) - ] - ).assert_success(); + ("MEMORY_PROFILER_OUTPUT", "backtrace.dat".into()), + ], + ) + .assert_success(); - let analysis = analyze( "backtrace", cwd.join( "backtrace.dat" ) ); - assert!( analysis.response.allocations.iter().any( |alloc| alloc.size == 123456 ) ); + let analysis = analyze("backtrace", cwd.join("backtrace.dat")); + assert!(analysis + .response + .allocations + .iter() + .any(|alloc| alloc.size == 123456)); } #[test] fn test_return_opt_u128() { let cwd = workdir(); - compile_with_rustc( "return-opt-u128.rs" ); + compile_with_rustc("return-opt-u128.rs"); run_on_target( &cwd, @@ -1338,18 +1505,23 @@ fn test_return_opt_u128() { &[ ("LD_PRELOAD", preload_path().into_os_string()), ("MEMORY_PROFILER_LOG", get_log_level()), - ("MEMORY_PROFILER_OUTPUT", "return-opt-u128.dat".into()) - ] - ).assert_success(); + ("MEMORY_PROFILER_OUTPUT", "return-opt-u128.dat".into()), + ], + ) + .assert_success(); - let analysis = analyze( "return-opt-u128", cwd.join( "return-opt-u128.dat" ) ); - assert!( analysis.response.allocations.iter().any( |alloc| alloc.size == 123456 ) ); + let analysis = analyze("return-opt-u128", cwd.join("return-opt-u128.dat")); + assert!(analysis + .response + .allocations + .iter() + .any(|alloc| alloc.size == 123456)); } #[test] fn test_return_f64() { let cwd = workdir(); - compile_with_rustc( "return-f64.rs" ); + compile_with_rustc("return-f64.rs"); run_on_target( &cwd, @@ -1358,19 +1530,24 @@ fn test_return_f64() { &[ ("LD_PRELOAD", preload_path().into_os_string()), ("MEMORY_PROFILER_LOG", get_log_level()), - ("MEMORY_PROFILER_OUTPUT", "return-f64.dat".into()) - ] - ).assert_success(); + ("MEMORY_PROFILER_OUTPUT", "return-f64.dat".into()), + ], + ) + .assert_success(); - let analysis = analyze( "return-f64", cwd.join( "return-f64.dat" ) ); - assert!( analysis.response.allocations.iter().any( |alloc| alloc.size == 123456 ) ); + let analysis = analyze("return-f64", cwd.join("return-f64.dat")); + assert!(analysis + .response + .allocations + .iter() + .any(|alloc| alloc.size == 123456)); } #[cfg(feature = "test-wasmtime")] #[cfg(target_arch = "x86_64")] #[test] fn test_wasmtime_linking() { - let cwd = compile_with_cargo( "wasmtime/linking" ); + let cwd = compile_with_cargo("wasmtime/linking"); run_on_target( &cwd, @@ -1379,15 +1556,21 @@ fn test_wasmtime_linking() { &[ ("LD_PRELOAD", preload_path().into_os_string()), ("MEMORY_PROFILER_LOG", get_log_level()), - ("MEMORY_PROFILER_OUTPUT", "linking.dat".into()) - ] - ).assert_success(); - - let analysis = analyze( "linking", cwd.join( "linking.dat" ) ); - let list: Vec< _ > = analysis.response.allocations.into_iter().filter( |alloc| is_from_function_fuzzy( alloc, "wasm_to_host_shim" ) ).collect(); - assert!( !list.is_empty() ); + ("MEMORY_PROFILER_OUTPUT", "linking.dat".into()), + ], + ) + .assert_success(); + + let analysis = analyze("linking", cwd.join("linking.dat")); + let list: Vec<_> = analysis + .response + .allocations + .into_iter() + .filter(|alloc| is_from_function_fuzzy(alloc, "wasm_to_host_shim")) + .collect(); + assert!(!list.is_empty()); for allocation in list { - assert!( is_from_function( &allocation, "main" ) ); + assert!(is_from_function(&allocation, "main")); } } @@ -1395,7 +1578,7 @@ fn test_wasmtime_linking() { #[cfg(target_arch = "x86_64")] #[test] fn test_wasmtime_interrupt() { - let cwd = compile_with_cargo( "wasmtime/interrupt" ); + let cwd = compile_with_cargo("wasmtime/interrupt"); run_on_target( &cwd, @@ -1404,15 +1587,21 @@ fn test_wasmtime_interrupt() { &[ ("LD_PRELOAD", preload_path().into_os_string()), ("MEMORY_PROFILER_LOG", get_log_level()), - ("MEMORY_PROFILER_OUTPUT", "interrupt.dat".into()) - ] - ).assert_success(); - - let analysis = analyze( "interrupt", cwd.join( "interrupt.dat" ) ); - let list: Vec< _ > = analysis.response.allocations.into_iter().filter( |alloc| is_from_function_fuzzy( alloc, "trap_handler" ) ).collect(); - assert!( !list.is_empty() ); + ("MEMORY_PROFILER_OUTPUT", "interrupt.dat".into()), + ], + ) + .assert_success(); + + let analysis = analyze("interrupt", cwd.join("interrupt.dat")); + let list: Vec<_> = analysis + .response + .allocations + .into_iter() + .filter(|alloc| is_from_function_fuzzy(alloc, "trap_handler")) + .collect(); + assert!(!list.is_empty()); for allocation in list { - assert!( is_from_function( &allocation, "main" ) ); + assert!(is_from_function(&allocation, "main")); } } @@ -1420,7 +1609,7 @@ fn test_wasmtime_interrupt() { fn test_cull() { let cwd = workdir(); - compile( "cull.c" ); + compile("cull.c"); run_on_target( &cwd, @@ -1431,39 +1620,48 @@ fn test_cull() { ("MEMORY_PROFILER_LOG", get_log_level()), ("MEMORY_PROFILER_OUTPUT", "memory-profiling-cull.dat".into()), ("MEMORY_PROFILER_CULL_TEMPORARY_ALLOCATIONS", "1".into()), - ("MEMORY_PROFILER_TEMPORARY_ALLOCATION_LIFETIME_THRESHOLD", "100".into()) - ] - ).assert_success(); + ( + "MEMORY_PROFILER_TEMPORARY_ALLOCATION_LIFETIME_THRESHOLD", + "100".into(), + ), + ], + ) + .assert_success(); - let analysis = analyze( "cull", cwd.join( "memory-profiling-cull.dat" ) ); - let mut iter = analysis.allocations_from_source( "cull.c" ); + let analysis = analyze("cull", cwd.join("memory-profiling-cull.dat")); + let mut iter = analysis.allocations_from_source("cull.c"); let a0 = iter.next().unwrap(); let a1 = iter.next().unwrap(); - assert!( a0.deallocation.is_some() ); - assert!( a1.deallocation.is_none() ); - assert_eq!( a0.size, 1235 ); - assert_eq!( a1.size, 1236 ); - - let g0 = analysis.groups.allocations.iter().find( |group| group.backtrace_id == a0.backtrace_id ).unwrap(); - assert_eq!( g0.all.leaked_count, 1 ); - assert_eq!( g0.all.allocated_count, 2 ); + assert!(a0.deallocation.is_some()); + assert!(a1.deallocation.is_none()); + assert_eq!(a0.size, 1235); + assert_eq!(a1.size, 1236); + + let g0 = analysis + .groups + .allocations + .iter() + .find(|group| group.backtrace_id == a0.backtrace_id) + .unwrap(); + assert_eq!(g0.all.leaked_count, 1); + assert_eq!(g0.all.allocated_count, 2); let a2 = iter.next().unwrap(); let a3 = iter.next().unwrap(); - assert_eq!( a2.size, 2000 ); - assert_eq!( a3.size, 3000 ); - assert_eq!( a2.chain_length, 2 ); - assert_eq!( a3.chain_length, 2 ); + assert_eq!(a2.size, 2000); + assert_eq!(a3.size, 3000); + assert_eq!(a2.chain_length, 2); + assert_eq!(a3.chain_length, 2); - assert_eq!( iter.next(), None ); + assert_eq!(iter.next(), None); } #[test] fn test_cross_thread_alloc_culled() { let cwd = workdir(); - compile_with_flags( "cross-thread-alloc.c", &["-o", "cross-thread-alloc-culled"] ); + compile_with_flags("cross-thread-alloc.c", &["-o", "cross-thread-alloc-culled"]); run_on_target( &cwd, @@ -1472,22 +1670,37 @@ fn test_cross_thread_alloc_culled() { &[ ("LD_PRELOAD", preload_path().into_os_string()), ("MEMORY_PROFILER_LOG", get_log_level()), - ("MEMORY_PROFILER_OUTPUT", "memory-profiling-cross-thread-alloc-culled.dat".into()), + ( + "MEMORY_PROFILER_OUTPUT", + "memory-profiling-cross-thread-alloc-culled.dat".into(), + ), ("MEMORY_PROFILER_CULL_TEMPORARY_ALLOCATIONS", "1".into()), - ("MEMORY_PROFILER_TEMPORARY_ALLOCATION_LIFETIME_THRESHOLD", "1000".into()) - ] - ).assert_success(); + ( + "MEMORY_PROFILER_TEMPORARY_ALLOCATION_LIFETIME_THRESHOLD", + "1000".into(), + ), + ], + ) + .assert_success(); - let analysis = analyze( "cross-thread-alloc-culled", cwd.join( "memory-profiling-cross-thread-alloc-culled.dat" ) ); - let mut iter = analysis.allocations_from_source( "cross-thread-alloc.c" ).filter( move |alloc| !is_from_function( alloc, "pthread_create" ) ); - assert!( iter.next().is_none() ); + let analysis = analyze( + "cross-thread-alloc-culled", + cwd.join("memory-profiling-cross-thread-alloc-culled.dat"), + ); + let mut iter = analysis + .allocations_from_source("cross-thread-alloc.c") + .filter(move |alloc| !is_from_function(alloc, "pthread_create")); + assert!(iter.next().is_none()); } #[test] fn test_cross_thread_alloc_non_culled() { let cwd = workdir(); - compile_with_flags( "cross-thread-alloc.c", &["-o", "cross-thread-alloc-non-culled"] ); + compile_with_flags( + "cross-thread-alloc.c", + &["-o", "cross-thread-alloc-non-culled"], + ); run_on_target( &cwd, @@ -1496,31 +1709,43 @@ fn test_cross_thread_alloc_non_culled() { &[ ("LD_PRELOAD", preload_path().into_os_string()), ("MEMORY_PROFILER_LOG", get_log_level()), - ("MEMORY_PROFILER_OUTPUT", "memory-profiling-cross-thread-alloc-non-culled.dat".into()), + ( + "MEMORY_PROFILER_OUTPUT", + "memory-profiling-cross-thread-alloc-non-culled.dat".into(), + ), ("MEMORY_PROFILER_CULL_TEMPORARY_ALLOCATIONS", "1".into()), - ("MEMORY_PROFILER_TEMPORARY_ALLOCATION_LIFETIME_THRESHOLD", "1".into()) - ] - ).assert_success(); + ( + "MEMORY_PROFILER_TEMPORARY_ALLOCATION_LIFETIME_THRESHOLD", + "1".into(), + ), + ], + ) + .assert_success(); - let analysis = analyze( "cross-thread-alloc-non-culled", cwd.join( "memory-profiling-cross-thread-alloc-non-culled.dat" ) ); - let mut iter = analysis.allocations_from_source( "cross-thread-alloc.c" ).filter( move |alloc| !is_from_function( alloc, "pthread_create" ) ); + let analysis = analyze( + "cross-thread-alloc-non-culled", + cwd.join("memory-profiling-cross-thread-alloc-non-culled.dat"), + ); + let mut iter = analysis + .allocations_from_source("cross-thread-alloc.c") + .filter(move |alloc| !is_from_function(alloc, "pthread_create")); let a0 = iter.next().unwrap(); let a1 = iter.next().unwrap(); let a2 = iter.next().unwrap(); - assert_eq!( a0.size, 1234 ); - assert_eq!( a1.size, 1235 ); - assert_eq!( a2.size, 1236 ); + assert_eq!(a0.size, 1234); + assert_eq!(a1.size, 1235); + assert_eq!(a2.size, 1236); - assert!( iter.next().is_none() ); + assert!(iter.next().is_none()); } #[test] fn test_track_spawned_children() { let cwd = workdir(); - compile_with_flags( "basic.c", &["-o", "basic-from-spawn-child"] ); - compile_with_flags( "spawn-child.c", &["-o", "spawn-child"] ); + compile_with_flags("basic.c", &["-o", "basic-from-spawn-child"]); + compile_with_flags("spawn-child.c", &["-o", "spawn-child"]); run_on_target( &cwd, @@ -1530,14 +1755,18 @@ fn test_track_spawned_children() { ("LD_PRELOAD", preload_path().into_os_string()), ("MEMORY_PROFILER_LOG", get_log_level()), ("MEMORY_PROFILER_TRACK_CHILD_PROCESSES", "1".into()), - ("MEMORY_PROFILER_OUTPUT", "memory-profiling-%e.dat".into()) - ] - ).assert_success(); + ("MEMORY_PROFILER_OUTPUT", "memory-profiling-%e.dat".into()), + ], + ) + .assert_success(); - check_allocations_basic_program( "basic-from-spawn-child", &cwd.join( "memory-profiling-basic-from-spawn-child.dat" ) ); + check_allocations_basic_program( + "basic-from-spawn-child", + &cwd.join("memory-profiling-basic-from-spawn-child.dat"), + ); - let analysis = analyze( "spawn-child", cwd.join( "memory-profiling-spawn-child.dat" ) ); - let mut iter = analysis.allocations_from_source( "spawn-child.c" ); + let analysis = analyze("spawn-child", cwd.join("memory-profiling-spawn-child.dat")); + let mut iter = analysis.allocations_from_source("spawn-child.c"); let a0 = iter.next().unwrap(); assert_eq!(a0.size, 10001); @@ -1546,9 +1775,9 @@ fn test_track_spawned_children() { assert_eq!(a1.size, 10003); } -fn check_allocations_basic_program( name: &str, path: &Path ) { - let analysis = analyze( name, path ); - let mut iter = analysis.allocations_from_source( "basic.c" ); +fn check_allocations_basic_program(name: &str, path: &Path) { + let analysis = analyze(name, path); + let mut iter = analysis.allocations_from_source("basic.c"); let a0 = iter.next().unwrap(); // malloc, leaked let a1 = iter.next().unwrap(); // malloc, freed @@ -1557,38 +1786,53 @@ fn check_allocations_basic_program( name: &str, path: &Path ) { let a4 = iter.next().unwrap(); // calloc, freed let a5 = iter.next().unwrap(); // posix_memalign, leaked - assert!( a0.deallocation.is_none() ); - assert!( a1.deallocation.is_some() ); - assert!( a2.deallocation.is_some() ); - assert!( a3.deallocation.is_none() ); - assert!( a4.deallocation.is_none() ); - assert!( a5.deallocation.is_none() ); + assert!(a0.deallocation.is_none()); + assert!(a1.deallocation.is_some()); + assert!(a2.deallocation.is_some()); + assert!(a3.deallocation.is_none()); + assert!(a4.deallocation.is_none()); + assert!(a5.deallocation.is_none()); - assert_eq!( a5.address % 65536, 0 ); + assert_eq!(a5.address % 65536, 0); - assert!( a0.size < a1.size ); - assert!( a1.size < a2.size ); - assert!( a2.size < a3.size ); - assert!( a3.size < a4.size ); - assert!( a4.size < a5.size ); + assert!(a0.size < a1.size); + assert!(a1.size < a2.size); + assert!(a2.size < a3.size); + assert!(a3.size < a4.size); + assert!(a4.size < a5.size); - assert_eq!( a0.thread, a1.thread ); - assert_eq!( a1.thread, a2.thread ); - assert_eq!( a2.thread, a3.thread ); - assert_eq!( a3.thread, a4.thread ); - assert_eq!( a4.thread, a5.thread ); + assert_eq!(a0.thread, a1.thread); + assert_eq!(a1.thread, a2.thread); + assert_eq!(a2.thread, a3.thread); + assert_eq!(a3.thread, a4.thread); + assert_eq!(a4.thread, a5.thread); assert_eq!( - a0.backtrace.iter().rev().skip( 1 ).next().unwrap().line.unwrap() + 1, - a1.backtrace.iter().rev().skip( 1 ).next().unwrap().line.unwrap() + a0.backtrace + .iter() + .rev() + .skip(1) + .next() + .unwrap() + .line + .unwrap() + + 1, + a1.backtrace + .iter() + .rev() + .skip(1) + .next() + .unwrap() + .line + .unwrap() ); - assert_eq!( a0.chain_length, 1 ); - assert_eq!( a1.chain_length, 1 ); - assert_eq!( a2.chain_length, 2 ); - assert_eq!( a3.chain_length, 2 ); - assert_eq!( a4.chain_length, 1 ); - assert_eq!( a5.chain_length, 1 ); + assert_eq!(a0.chain_length, 1); + assert_eq!(a1.chain_length, 1); + assert_eq!(a2.chain_length, 2); + assert_eq!(a3.chain_length, 2); + assert_eq!(a4.chain_length, 1); + assert_eq!(a5.chain_length, 1); - assert_eq!( iter.next(), None ); + assert_eq!(iter.next(), None); } diff --git a/integration-tests/src/utils.rs b/integration-tests/src/utils.rs index ddd5b1fa..cae75fec 100644 --- a/integration-tests/src/utils.rs +++ b/integration-tests/src/utils.rs @@ -1,51 +1,63 @@ -use std::path::{Path, PathBuf}; -use std::process::{Command, ExitStatus, Child, Stdio}; use std::ffi::OsStr; use std::fs; -use std::io::{self, Read, BufRead, BufReader}; -use std::thread; -use std::sync::{Mutex, Arc}; +use std::io::{self, BufRead, BufReader, Read}; use std::mem; +use std::path::{Path, PathBuf}; +use std::process::{Child, Command, ExitStatus, Stdio}; +use std::sync::{Arc, Mutex}; +use std::thread; use std::time::{Duration, Instant}; pub static EMPTY_ARGS: &[&str] = &[]; pub static EMPTY_ENV: &[(&str, &str)] = &[]; -fn run_internal< 'a, R, I, N, C, E, S, P, Q, F >( cwd: C, executable: E, args: I, envs: N, callback: F ) -> R - where I: IntoIterator< Item = S >, - N: IntoIterator< Item = &'a (P, Q) >, - C: AsRef< Path >, - E: AsRef< OsStr >, - S: AsRef< OsStr >, - P: AsRef< OsStr > + 'a, - Q: AsRef< OsStr > + 'a, - F: FnOnce( Command ) -> Result< R, io::Error > +fn run_internal<'a, R, I, N, C, E, S, P, Q, F>( + cwd: C, + executable: E, + args: I, + envs: N, + callback: F, +) -> R +where + I: IntoIterator, + N: IntoIterator, + C: AsRef, + E: AsRef, + S: AsRef, + P: AsRef + 'a, + Q: AsRef + 'a, + F: FnOnce(Command) -> Result, { let executable = executable.as_ref(); - let args: Vec< _ > = args.into_iter().map( |arg| arg.as_ref().to_owned() ).collect(); + let args: Vec<_> = args + .into_iter() + .map(|arg| arg.as_ref().to_owned()) + .collect(); let mut invocation: String = executable.to_string_lossy().into_owned(); for arg in &args { - invocation.push_str( " " ); - invocation.push_str( &arg.to_string_lossy() ); + invocation.push_str(" "); + invocation.push_str(&arg.to_string_lossy()); } - eprintln!( "> {}", invocation ); + eprintln!("> {}", invocation); - let mut cmd = Command::new( executable ); - cmd.args( args ); - cmd.current_dir( cwd ); + let mut cmd = Command::new(executable); + cmd.args(args); + cmd.current_dir(cwd); for (key, value) in envs.into_iter() { - cmd.env( key, value ); + cmd.env(key, value); } - match callback( cmd ) { - Ok( value ) => { - value - }, - Err( error ) => { - panic!( "Failed to launch `{}`: {:?}", executable.to_string_lossy(), error ); + match callback(cmd) { + Ok(value) => value, + Err(error) => { + panic!( + "Failed to launch `{}`: {:?}", + executable.to_string_lossy(), + error + ); } } } @@ -53,58 +65,61 @@ fn run_internal< 'a, R, I, N, C, E, S, P, Q, F >( cwd: C, executable: E, args: I #[must_use] pub struct CommandResult { status: ExitStatus, - output: String + output: String, } impl CommandResult { - pub fn assert_success( self ) { + pub fn assert_success(self) { if !self.status.success() { - panic!( "Command exited with a status of {:?}!", self.status.code() ); + panic!("Command exited with a status of {:?}!", self.status.code()); } } #[allow(dead_code)] - pub fn assert_failure( self ) { + pub fn assert_failure(self) { if self.status.success() { - panic!( "Command unexpectedly succeeded!" ); + panic!("Command unexpectedly succeeded!"); } } #[allow(dead_code)] - pub fn output( &self ) -> &str { + pub fn output(&self) -> &str { &self.output } } -fn print_stream< T: Read + Send + 'static >( fp: T, output: Arc< Mutex< String > > ) -> thread::JoinHandle< () > { - let fp = BufReader::new( fp ); - thread::spawn( move || { +fn print_stream( + fp: T, + output: Arc>, +) -> thread::JoinHandle<()> { + let fp = BufReader::new(fp); + thread::spawn(move || { for line in fp.lines() { let line = match line { - Ok( line ) => line, - Err( _ ) => break + Ok(line) => line, + Err(_) => break, }; let mut output = output.lock().unwrap(); - output.push_str( &line ); - output.push_str( "\n" ); + output.push_str(&line); + output.push_str("\n"); } }) } pub struct ChildHandle { - output: Arc< Mutex< String > >, - stdout_join: Option< thread::JoinHandle< () > >, - stderr_join: Option< thread::JoinHandle< () > >, - child: Child + output: Arc>, + stdout_join: Option>, + stderr_join: Option>, + child: Child, } impl ChildHandle { - pub fn wait( self ) -> CommandResult { - self.wait_for( Duration::from_secs( 60 ) ) + pub fn wait(self) -> CommandResult { + self.wait_for(Duration::from_secs(60)) } - pub fn wait_for( mut self, duration: Duration ) -> CommandResult { + pub fn wait_for(mut self, duration: Duration) -> CommandResult { let start = Instant::now(); let mut status = None; while start.elapsed() < duration { @@ -115,115 +130,126 @@ impl ChildHandle { } let status = match status { - Some( status ) => status, + Some(status) => status, None => { - panic!( "Timeout while waiting for the child process to exit!" ); + panic!("Timeout while waiting for the child process to exit!"); } }; let output = self.flush_output(); - CommandResult { - status, - output - } + CommandResult { status, output } } - pub fn pid( &self ) -> u32 { + pub fn pid(&self) -> u32 { self.child.id() } - fn flush_output( &mut self ) -> String { - if let Some( stdout_join ) = self.stdout_join.take() { + fn flush_output(&mut self) -> String { + if let Some(stdout_join) = self.stdout_join.take() { let _ = stdout_join.join(); } - if let Some( stderr_join ) = self.stderr_join.take() { + if let Some(stderr_join) = self.stderr_join.take() { let _ = stderr_join.join(); } let mut output = String::new(); - mem::swap( &mut output, &mut self.output.lock().unwrap() ); - print!( "{}", output ); + mem::swap(&mut output, &mut self.output.lock().unwrap()); + print!("{}", output); output } } impl Drop for ChildHandle { - fn drop( &mut self ) { + fn drop(&mut self) { let _ = self.child.kill(); self.flush_output(); } } -pub fn run_in_the_background< C, E, S, P, Q >( cwd: C, executable: E, args: &[S], envs: &[(P, Q)] ) -> ChildHandle - where C: AsRef< Path >, - E: AsRef< OsStr >, - S: AsRef< OsStr >, - P: AsRef< OsStr >, - Q: AsRef< OsStr > +pub fn run_in_the_background( + cwd: C, + executable: E, + args: &[S], + envs: &[(P, Q)], +) -> ChildHandle +where + C: AsRef, + E: AsRef, + S: AsRef, + P: AsRef, + Q: AsRef, { - run_internal( cwd, executable, args, envs, |mut cmd| { - let output = Arc::new( Mutex::new( String::new() ) ); - cmd.stdin( Stdio::null() ); - cmd.stdout( Stdio::piped() ); - cmd.stderr( Stdio::piped() ); + run_internal(cwd, executable, args, envs, |mut cmd| { + let output = Arc::new(Mutex::new(String::new())); + cmd.stdin(Stdio::null()); + cmd.stdout(Stdio::piped()); + cmd.stderr(Stdio::piped()); let mut child = cmd.spawn()?; - let stdout_join = print_stream( child.stdout.take().unwrap(), output.clone() ); - let stderr_join = print_stream( child.stderr.take().unwrap(), output.clone() ); + let stdout_join = print_stream(child.stdout.take().unwrap(), output.clone()); + let stderr_join = print_stream(child.stderr.take().unwrap(), output.clone()); - Ok( ChildHandle { + Ok(ChildHandle { output, - stdout_join: Some( stdout_join ), - stderr_join: Some( stderr_join ), - child + stdout_join: Some(stdout_join), + stderr_join: Some(stderr_join), + child, }) }) } -pub fn run< C, E, S, P, Q >( cwd: C, executable: E, args: &[S], envs: &[(P, Q)] ) -> CommandResult - where C: AsRef< Path >, - E: AsRef< OsStr >, - S: AsRef< OsStr >, - P: AsRef< OsStr >, - Q: AsRef< OsStr > +pub fn run(cwd: C, executable: E, args: &[S], envs: &[(P, Q)]) -> CommandResult +where + C: AsRef, + E: AsRef, + S: AsRef, + P: AsRef, + Q: AsRef, { - run_in_the_background( cwd, executable, args, envs ).wait() + run_in_the_background(cwd, executable, args, envs).wait() } -pub fn run_with_timeout< C, E, S, P, Q >( cwd: C, executable: E, args: &[S], envs: &[(P, Q)], timeout: Duration ) -> CommandResult - where C: AsRef< Path >, - E: AsRef< OsStr >, - S: AsRef< OsStr >, - P: AsRef< OsStr >, - Q: AsRef< OsStr > +pub fn run_with_timeout( + cwd: C, + executable: E, + args: &[S], + envs: &[(P, Q)], + timeout: Duration, +) -> CommandResult +where + C: AsRef, + E: AsRef, + S: AsRef, + P: AsRef, + Q: AsRef, { - run_in_the_background( cwd, executable, args, envs ).wait_for( timeout ) + run_in_the_background(cwd, executable, args, envs).wait_for(timeout) } -pub fn assert_file_exists< P: AsRef< Path > >( path: P ) { +pub fn assert_file_exists>(path: P) { let path = path.as_ref(); if !path.exists() { - panic!( "File {:?} doesn't exist", path ); + panic!("File {:?} doesn't exist", path); } } -pub fn assert_file_missing< P: AsRef< Path > >( path: P ) { +pub fn assert_file_missing>(path: P) { let path = path.as_ref(); if path.exists() { - panic!( "File {:?} exists", path ); + panic!("File {:?} exists", path); } } -pub fn dir_entries< P: AsRef< Path > >( path: P ) -> Result< Vec< PathBuf >, io::Error > { +pub fn dir_entries>(path: P) -> Result, io::Error> { let mut output = Vec::new(); let path = path.as_ref(); - for entry in fs::read_dir( path )? { + for entry in fs::read_dir(path)? { let entry = entry?; - output.push( entry.path() ); + output.push(entry.path()); } - Ok( output ) + Ok(output) } diff --git a/jemallocator/jemalloc-sys/Cargo.toml b/jemallocator/jemalloc-sys/Cargo.toml index 058d9c77..d8271798 100644 --- a/jemallocator/jemalloc-sys/Cargo.toml +++ b/jemallocator/jemalloc-sys/Cargo.toml @@ -31,11 +31,11 @@ test = false bench = false [dependencies] -libc = { version = "^0.2.8", default-features = false } +libc = { version = "^0.2.140", default-features = false } [build-dependencies] -cc = "^1.0.13" -fs_extra = "^1.1" +cc = "^1.0.79" +fs_extra = "^1.3" [features] default = ["background_threads_runtime_support"] diff --git a/lz4-compress/Cargo.toml b/lz4-compress/Cargo.toml index 76dd1bac..2210ec08 100644 --- a/lz4-compress/Cargo.toml +++ b/lz4-compress/Cargo.toml @@ -11,4 +11,4 @@ exclude = ["target", "Cargo.lock"] [dependencies] byteorder = "1" -quick-error = "1" +quick-error = "2" diff --git a/lz4-compress/src/compress.rs b/lz4-compress/src/compress.rs index b761d060..f2353f54 100644 --- a/lz4-compress/src/compress.rs +++ b/lz4-compress/src/compress.rs @@ -4,7 +4,7 @@ //! high performance. It has fixed memory usage, which contrary to other approachs, makes it less //! memory hungry. -use byteorder::{NativeEndian, ByteOrder}; +use byteorder::{ByteOrder, NativeEndian}; /// Duplication dictionary size. /// @@ -109,7 +109,9 @@ fn each_u32_window(slice: &[u8], mut steps: usize, mut callback: impl FnMut(u32) #[test] fn test_each_u32_window() { - let slice = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA]; + let slice = [ + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, + ]; fn run(slice: &[u8], steps: usize) -> Vec { let mut output = Vec::new(); each_u32_window(slice, steps, |value| { @@ -155,12 +157,11 @@ fn common_prefix_length(lhs: &[u8], rhs: &[u8]) -> usize { ap = ap.add(4); bp = bp.add(4); if a != b { - let zeros = - if cfg!(target_endian = "little") { - (a ^ b).trailing_zeros() - } else { - (a ^ b).leading_zeros() - }; + let zeros = if cfg!(target_endian = "little") { + (a ^ b).trailing_zeros() + } else { + (a ^ b).leading_zeros() + }; count += zeros as usize / 8; return count; } @@ -221,7 +222,7 @@ impl<'a> Encoder<'a> { let input = self.input.get(self.cur..self.cur + steps + 7); let input = match input { Some(input) => input, - None => return self.go_forward_slow(steps) + None => return self.go_forward_slow(steps), }; each_u32_window(input, steps, |x| { @@ -302,8 +303,9 @@ impl<'a> Encoder<'a> { // - We can address up to 16-bit offset, hence we are only able to address the candidate if // its offset is less than or equals to 0xFFFF. if candidate != !0 - && self.get_batch(candidate) == self.get_batch_at_cursor() - && self.cur - candidate <= 0xFFFF { + && self.get_batch(candidate) == self.get_batch_at_cursor() + && self.cur - candidate <= 0xFFFF + { // Calculate the "extension bytes", i.e. the duplicate bytes beyond the batch. These // are the number of prefix bytes shared between the match and needle. let lhs = &self.input[self.cur + 4..]; @@ -318,7 +320,9 @@ impl<'a> Encoder<'a> { offset: (self.cur - candidate) as u16, extra_bytes: ext, }) - } else { None } + } else { + None + } } /// Write an integer to the output in LSIC format. @@ -408,7 +412,8 @@ impl<'a> Encoder<'a> { } // Now, write the actual literals. - self.output.extend_from_slice(&self.input[start..start + block.lit_len]); + self.output + .extend_from_slice(&self.input[start..start + block.lit_len]); if let Some(Duplicate { offset, .. }) = block.dup { // Wait! There's more. Now, we encode the duplicates section. @@ -436,7 +441,8 @@ pub fn compress_into(input: &[u8], output: &mut Vec) { output: output, cur: 0, dict: [!0; DICTIONARY_SIZE], - }.complete(); + } + .complete(); } /// Compress all bytes of `input`. diff --git a/lz4-compress/src/decompress.rs b/lz4-compress/src/decompress.rs index c298b2d9..35e52a7b 100644 --- a/lz4-compress/src/decompress.rs +++ b/lz4-compress/src/decompress.rs @@ -1,6 +1,6 @@ //! The decompression algorithm. -use byteorder::{LittleEndian, ByteOrder}; +use byteorder::{ByteOrder, LittleEndian}; quick_error! { /// An error representing invalid compressed data. @@ -8,11 +8,11 @@ quick_error! { pub enum Error { /// Expected another byte, but none found. ExpectedAnotherByte { - description("Expected another byte, found none.") + display("Expected another byte, found none.") } /// Deduplication offset out of bounds (not in buffer). OffsetOutOfBounds { - description("The offset to copy is not contained in the decompressed buffer.") + display("The offset to copy is not contained in the decompressed buffer.") } } } @@ -246,7 +246,9 @@ impl<'a> Decoder<'a> { // If the input stream is emptied, we break out of the loop. This is only the case // in the end of the stream, since the block is intact otherwise. - if self.input.is_empty() { break; } + if self.input.is_empty() { + break; + } // Now, we read the duplicates section. self.read_duplicate_section()?; @@ -263,7 +265,8 @@ pub fn decompress_into(input: &[u8], output: &mut Vec) -> Result<(), Error> input: input, output: output, token: 0, - }.complete()?; + } + .complete()?; Ok(()) } @@ -289,7 +292,10 @@ mod test { #[test] fn multiple_repeated_blocks() { - assert_eq!(decompress(&[0x11, b'a', 1, 0, 0x22, b'b', b'c', 2, 0]).unwrap(), b"aaaaaabcbcbcbc"); + assert_eq!( + decompress(&[0x11, b'a', 1, 0, 0x22, b'b', b'c', 2, 0]).unwrap(), + b"aaaaaabcbcbcbc" + ); } #[test] diff --git a/lz4-compress/src/lib.rs b/lz4-compress/src/lib.rs index 8aa03429..23639389 100644 --- a/lz4-compress/src/lib.rs +++ b/lz4-compress/src/lib.rs @@ -8,16 +8,10 @@ extern crate byteorder; #[macro_use] extern crate quick_error; -mod decompress; mod compress; +mod decompress; #[cfg(test)] mod tests; -pub use decompress::{ - decompress, - decompress_into -}; -pub use compress::{ - compress, - compress_into -}; +pub use compress::{compress, compress_into}; +pub use decompress::{decompress, decompress_into}; diff --git a/lz4-compress/src/main.rs b/lz4-compress/src/main.rs index e43f4313..2be59c46 100644 --- a/lz4-compress/src/main.rs +++ b/lz4-compress/src/main.rs @@ -1,7 +1,7 @@ extern crate lz4_compress as lz4; +use std::io::{self, Read, Write}; use std::{env, process}; -use std::io::{self, Write, Read}; /// The help page for this command. const HELP: &'static [u8] = br#" @@ -28,27 +28,35 @@ fn main() { "-c" => { // Read stream from stdin. let mut vec = Vec::new(); - io::stdin().read_to_end(&mut vec).expect("Failed to read stdin"); + io::stdin() + .read_to_end(&mut vec) + .expect("Failed to read stdin"); // Compress it and write the result to stdout. - io::stdout().write(&lz4::compress(&vec)).expect("Failed to write to stdout"); - }, + io::stdout() + .write(&lz4::compress(&vec)) + .expect("Failed to write to stdout"); + } "-d" => { // Read stream from stdin. let mut vec = Vec::new(); - io::stdin().read_to_end(&mut vec).expect("Failed to read stdin"); + io::stdin() + .read_to_end(&mut vec) + .expect("Failed to read stdin"); // Decompress the input. let decompressed = lz4::decompress(&vec).expect("Compressed data contains errors"); // Write the decompressed buffer to stdout. - io::stdout().write(&decompressed).expect("Failed to write to stdout"); - }, + io::stdout() + .write(&decompressed) + .expect("Failed to write to stdout"); + } // If no valid arguments are given, we print the help page. _ => { io::stdout().write(HELP).expect("Failed to write to stdout"); process::exit(1); - }, + } } } diff --git a/lz4-compress/src/tests.rs b/lz4-compress/src/tests.rs index 26baf471..2d37caf9 100644 --- a/lz4-compress/src/tests.rs +++ b/lz4-compress/src/tests.rs @@ -2,14 +2,17 @@ use std::str; -use {decompress, compress}; +use {compress, decompress}; /// Test that the compressed string decompresses to the original string. fn inverse(s: &str) { let compressed = compress(s.as_bytes()); println!("Compressed '{}' into {:?}", s, compressed); let decompressed = decompress(&compressed).unwrap(); - println!("Decompressed it into {:?}", str::from_utf8(&decompressed).unwrap()); + println!( + "Decompressed it into {:?}", + str::from_utf8(&decompressed).unwrap() + ); assert_eq!(decompressed, s.as_bytes()); } @@ -82,5 +85,11 @@ fn big_compression() { #[test] fn compression_output() { let output = compress(b"Random data, a, aa, aaa, aaaa, aaaaa, aaaaaa, aaaaaaa, aaaaaaaa"); - assert_eq!(output, &[208, 82, 97, 110, 100, 111, 109, 32, 100, 97, 116, 97, 44, 32, 3, 0, 1, 4, 0, 2, 5, 0, 3, 6, 0, 4, 7, 0, 5, 8, 0, 6, 9, 0, 16, 97][..]); + assert_eq!( + output, + &[ + 208, 82, 97, 110, 100, 111, 109, 32, 100, 97, 116, 97, 44, 32, 3, 0, 1, 4, 0, 2, 5, 0, + 3, 6, 0, 4, 7, 0, 5, 8, 0, 6, 9, 0, 16, 97 + ][..] + ); } diff --git a/preload/Cargo.toml b/preload/Cargo.toml index 5fb579b5..d2a0eb5c 100644 --- a/preload/Cargo.toml +++ b/preload/Cargo.toml @@ -15,11 +15,11 @@ libc = "0.2" sc = { version = "0.2", optional = true } lazy_static = "1" log = "0.4" -glob = "0.2" -lru = { version = "0.6", default-features = false } +glob = "0.3" +lru = { version = "0.10", default-features = false } tikv-jemalloc-sys = { path = "../jemallocator/jemalloc-sys", default-features = false } mimalloc = { path = "../mimalloc_rust", default-features = false } -goblin = "0.0.24" +goblin = "0.6.1" smallvec = { version = "1", features = ["union"] } ahash = "0.8" hashbrown = "0.13" diff --git a/preload/src/allocation_tracker.rs b/preload/src/allocation_tracker.rs index a333751a..24f7ca4c 100644 --- a/preload/src/allocation_tracker.rs +++ b/preload/src/allocation_tracker.rs @@ -1,82 +1,91 @@ +use std::collections::HashMap; use std::num::NonZeroUsize; -use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; -use std::collections::HashMap; +use std::sync::Arc; use parking_lot::{Mutex, RwLock}; -use common::Timestamp; use common::event::AllocationId; +use common::Timestamp; +use crate::event::{InternalAllocation, InternalAllocationId, InternalEvent}; use crate::global::StrongThreadHandle; use crate::unwind::Backtrace; -use crate::event::{InternalAllocation, InternalAllocationId, InternalEvent}; pub struct BufferedAllocation { pub timestamp: Timestamp, pub allocation: InternalAllocation, - pub backtrace: Backtrace + pub backtrace: Backtrace, } pub struct AllocationBucket { pub id: AllocationId, - pub events: smallvec::SmallVec< [BufferedAllocation; 1] > + pub events: smallvec::SmallVec<[BufferedAllocation; 1]>, } impl AllocationBucket { - fn is_long_lived( &self, now: Timestamp ) -> bool { - now.as_usecs() >= self.events[0].timestamp.as_usecs() + crate::opt::get().temporary_allocation_lifetime_threshold * 1000 + fn is_long_lived(&self, now: Timestamp) -> bool { + now.as_usecs() + >= self.events[0].timestamp.as_usecs() + + crate::opt::get().temporary_allocation_lifetime_threshold * 1000 } } #[derive(Default)] struct AllocationTrackerRegistry { - per_thread: HashMap< u64, AllocationTracker, crate::nohash::NoHash >, - global: Mutex< crate::ordered_map::OrderedMap< (u64, u64), AllocationBucket, crate::nohash::NoHash > > + per_thread: HashMap, + global: + Mutex>, } -static ENABLED: AtomicBool = AtomicBool::new( false ); +static ENABLED: AtomicBool = AtomicBool::new(false); lazy_static! { - static ref ALLOCATION_TRACKER_REGISTRY: RwLock< AllocationTrackerRegistry > = Default::default(); + static ref ALLOCATION_TRACKER_REGISTRY: RwLock = Default::default(); } #[derive(Default)] struct AllocationTrackerState { - buckets: crate::ordered_map::OrderedMap< u64, AllocationBucket, crate::nohash::NoHash >, + buckets: crate::ordered_map::OrderedMap, } -fn get_shard_key( id: AllocationId ) -> usize { +fn get_shard_key(id: AllocationId) -> usize { id.thread as usize } #[repr(transparent)] -pub struct AllocationTracker( Arc< Mutex< AllocationTrackerState > > ); +pub struct AllocationTracker(Arc>); pub fn initialize() { - ENABLED.store( crate::opt::get().cull_temporary_allocations, Ordering::SeqCst ); + ENABLED.store( + crate::opt::get().cull_temporary_allocations, + Ordering::SeqCst, + ); let _ = ALLOCATION_TRACKER_REGISTRY.write(); } -pub fn on_thread_created( unique_tid: u64 ) -> AllocationTracker { - let tracker = AllocationTracker( Arc::new( Mutex::new( AllocationTrackerState::default() ) ) ); +pub fn on_thread_created(unique_tid: u64) -> AllocationTracker { + let tracker = AllocationTracker(Arc::new(Mutex::new(AllocationTrackerState::default()))); - ALLOCATION_TRACKER_REGISTRY.write().per_thread.insert( unique_tid, AllocationTracker( tracker.0.clone() ) ); + ALLOCATION_TRACKER_REGISTRY + .write() + .per_thread + .insert(unique_tid, AllocationTracker(tracker.0.clone())); tracker } -pub fn on_thread_destroyed( unique_tid: u64 ) { +pub fn on_thread_destroyed(unique_tid: u64) { let mut registry = ALLOCATION_TRACKER_REGISTRY.write(); - if let Some( local_tracker ) = registry.per_thread.remove( &unique_tid ) { + if let Some(local_tracker) = registry.per_thread.remove(&unique_tid) { let timestamp = crate::timestamp::get_timestamp(); let mut local_tracker = local_tracker.0.lock(); - flush_pending( &mut local_tracker.buckets, timestamp ); + flush_pending(&mut local_tracker.buckets, timestamp); let mut global_tracker = registry.global.lock(); - flush_pending( &mut global_tracker, timestamp ); + flush_pending(&mut global_tracker, timestamp); - while let Some( (allocation_id, bucket) ) = local_tracker.buckets.pop_front() { - global_tracker.insert( (unique_tid, allocation_id), bucket ); + while let Some((allocation_id, bucket)) = local_tracker.buckets.pop_front() { + global_tracker.insert((unique_tid, allocation_id), bucket); } } } @@ -86,55 +95,58 @@ pub fn on_tick() { let timestamp = crate::timestamp::get_timestamp(); { let mut global_tracker = registry.global.lock(); - flush_pending( &mut global_tracker, timestamp ); + flush_pending(&mut global_tracker, timestamp); } for tracker in registry.per_thread.values() { let mut local_tracker = tracker.0.lock(); - flush_pending( &mut local_tracker.buckets, timestamp ); + flush_pending(&mut local_tracker.buckets, timestamp); } } pub fn on_exit() { - ENABLED.store( false, Ordering::SeqCst ); + ENABLED.store(false, Ordering::SeqCst); let mut buckets = Vec::new(); let mut registry = ALLOCATION_TRACKER_REGISTRY.write(); - for (_, tracker) in std::mem::take( &mut registry.per_thread ) { + for (_, tracker) in std::mem::take(&mut registry.per_thread) { let mut tracker = tracker.0.lock(); - while let Some( (_, bucket) ) = tracker.buckets.pop_front() { - buckets.push( bucket ); + while let Some((_, bucket)) = tracker.buckets.pop_front() { + buckets.push(bucket); } } { let mut global = registry.global.lock(); - while let Some( (_, bucket) ) = global.pop_front() { - buckets.push( bucket ); + while let Some((_, bucket)) = global.pop_front() { + buckets.push(bucket); } } - info!( "Flushing {} bucket(s) on exit", buckets.len() ); + info!("Flushing {} bucket(s) on exit", buckets.len()); for bucket in buckets { - crate::event::send_event_throttled_sharded( get_shard_key( bucket.id ), move || { - InternalEvent::AllocationBucket( bucket ) + crate::event::send_event_throttled_sharded(get_shard_key(bucket.id), move || { + InternalEvent::AllocationBucket(bucket) }); } } -fn flush_pending< K >( - allocations: &mut crate::ordered_map::OrderedMap< K, AllocationBucket, crate::nohash::NoHash >, - timestamp: Timestamp -) where K: Copy + PartialEq + Eq + std::hash::Hash { - let temporary_allocation_pending_threshold = crate::opt::get().temporary_allocation_pending_threshold.unwrap_or( !0 ); - while let Some( key ) = allocations.front_key() { - let should_flush = - allocations.len() > temporary_allocation_pending_threshold || - allocations.get( &key ).unwrap().is_long_lived( timestamp ); +fn flush_pending( + allocations: &mut crate::ordered_map::OrderedMap, + timestamp: Timestamp, +) where + K: Copy + PartialEq + Eq + std::hash::Hash, +{ + let temporary_allocation_pending_threshold = crate::opt::get() + .temporary_allocation_pending_threshold + .unwrap_or(!0); + while let Some(key) = allocations.front_key() { + let should_flush = allocations.len() > temporary_allocation_pending_threshold + || allocations.get(&key).unwrap().is_long_lived(timestamp); if should_flush { - let bucket = allocations.remove( &key ).unwrap(); - crate::event::send_event_throttled_sharded( get_shard_key( bucket.id ), move || { - InternalEvent::AllocationBucket( bucket ) + let bucket = allocations.remove(&key).unwrap(); + crate::event::send_event_throttled_sharded(get_shard_key(bucket.id), move || { + InternalEvent::AllocationBucket(bucket) }); } else { break; @@ -146,52 +158,55 @@ pub fn on_allocation( id: InternalAllocationId, allocation: InternalAllocation, backtrace: Backtrace, - thread: StrongThreadHandle + thread: StrongThreadHandle, ) { let timestamp = crate::timestamp::get_timestamp(); let id: AllocationId = id.into(); - debug_assert_eq!( id.thread, thread.unique_tid() ); + debug_assert_eq!(id.thread, thread.unique_tid()); if thread.is_dead() { let mut zombie_events = thread.zombie_events().lock(); - zombie_events.push( - InternalEvent::Alloc { - id, - timestamp, - allocation, - backtrace, - } - ); + zombie_events.push(InternalEvent::Alloc { + id, + timestamp, + allocation, + backtrace, + }); return; - } else if ENABLED.load( Ordering::Relaxed ) && !id.is_untracked() { + } else if ENABLED.load(Ordering::Relaxed) && !id.is_untracked() { let mut bucket = AllocationBucket { id, - events: Default::default() + events: Default::default(), }; let address = allocation.address; - bucket.events.push( BufferedAllocation { timestamp, allocation, backtrace } ); + bucket.events.push(BufferedAllocation { + timestamp, + allocation, + backtrace, + }); let mut tracker = thread.allocation_tracker().0.lock(); - if tracker.buckets.insert( id.allocation, bucket ).is_some() { - error!( "Duplicate allocation 0x{:08X} with ID {}; this should never happen", address, id ); + if tracker.buckets.insert(id.allocation, bucket).is_some() { + error!( + "Duplicate allocation 0x{:08X} with ID {}; this should never happen", + address, id + ); } - flush_pending( &mut tracker.buckets, timestamp ); + flush_pending(&mut tracker.buckets, timestamp); return; } - crate::event::send_event_throttled_sharded( get_shard_key( id ), move || { - InternalEvent::Alloc { - id, - timestamp, - allocation, - backtrace, - } + crate::event::send_event_throttled_sharded(get_shard_key(id), move || InternalEvent::Alloc { + id, + timestamp, + allocation, + backtrace, }); - std::mem::drop( thread ); + std::mem::drop(thread); } pub fn on_reallocation( @@ -199,34 +214,33 @@ pub fn on_reallocation( old_address: NonZeroUsize, allocation: InternalAllocation, backtrace: Backtrace, - thread: StrongThreadHandle + thread: StrongThreadHandle, ) { let timestamp = crate::timestamp::get_timestamp(); let id: AllocationId = id.into(); if id.is_invalid() { // TODO: If we're culling temporary allocations try to find one with the same address and flush it. error!( "Allocation 0x{:08X} with invalid ID {} was reallocated; this should never happen; you probably have an out-of-bounds write somewhere", old_address, id ); - } if thread.is_dead() { + } + if thread.is_dead() { let mut zombie_events = thread.zombie_events().lock(); - zombie_events.push( - InternalEvent::Realloc { - id, - timestamp, - old_address, - allocation, - backtrace, - } - ); + zombie_events.push(InternalEvent::Realloc { + id, + timestamp, + old_address, + allocation, + backtrace, + }); return; - } else if !thread.is_dead() && ENABLED.load( Ordering::Relaxed ) && !id.is_untracked() { + } else if !thread.is_dead() && ENABLED.load(Ordering::Relaxed) && !id.is_untracked() { fn emit( bucket: &mut AllocationBucket, timestamp: Timestamp, id: AllocationId, old_address: NonZeroUsize, allocation: InternalAllocation, - backtrace: Backtrace + backtrace: Backtrace, ) { if bucket.events.last().unwrap().allocation.address != old_address { error!( @@ -237,99 +251,103 @@ pub fn on_reallocation( ); } - bucket.events.push( BufferedAllocation { timestamp, allocation, backtrace } ); + bucket.events.push(BufferedAllocation { + timestamp, + allocation, + backtrace, + }); } if id.thread == thread.unique_tid() { let mut tracker = thread.allocation_tracker().0.lock(); - flush_pending( &mut tracker.buckets, timestamp ); + flush_pending(&mut tracker.buckets, timestamp); - if let Some( bucket ) = tracker.buckets.get_mut( &id.allocation ) { - return emit( bucket, timestamp, id, old_address, allocation, backtrace ); + if let Some(bucket) = tracker.buckets.get_mut(&id.allocation) { + return emit(bucket, timestamp, id, old_address, allocation, backtrace); } } else { let registry = ALLOCATION_TRACKER_REGISTRY.read(); - if let Some( registry ) = registry.per_thread.get( &id.thread ) { + if let Some(registry) = registry.per_thread.get(&id.thread) { let mut tracker = registry.0.lock(); - flush_pending( &mut tracker.buckets, timestamp ); + flush_pending(&mut tracker.buckets, timestamp); - if let Some( bucket ) = tracker.buckets.get_mut( &id.allocation ) { - return emit( bucket, timestamp, id, old_address, allocation, backtrace ); + if let Some(bucket) = tracker.buckets.get_mut(&id.allocation) { + return emit(bucket, timestamp, id, old_address, allocation, backtrace); } } else { let mut buckets = registry.global.lock(); - flush_pending( &mut buckets, timestamp ); + flush_pending(&mut buckets, timestamp); - if let Some( bucket ) = buckets.get_mut( &(id.thread, id.allocation) ) { - return emit( bucket, timestamp, id, old_address, allocation, backtrace ); + if let Some(bucket) = buckets.get_mut(&(id.thread, id.allocation)) { + return emit(bucket, timestamp, id, old_address, allocation, backtrace); } } } } - crate::event::send_event_throttled_sharded( get_shard_key( id ), move || { - InternalEvent::Realloc { - id, - timestamp, - old_address, - allocation, - backtrace, - } + crate::event::send_event_throttled_sharded(get_shard_key(id), move || InternalEvent::Realloc { + id, + timestamp, + old_address, + allocation, + backtrace, }); - std::mem::drop( thread ); + std::mem::drop(thread); } pub fn on_free( id: InternalAllocationId, address: NonZeroUsize, - backtrace: Option< Backtrace >, - thread: StrongThreadHandle + backtrace: Option, + thread: StrongThreadHandle, ) { let timestamp = crate::timestamp::get_timestamp(); let id: AllocationId = id.into(); if id.is_invalid() { // TODO: If we're culling temporary allocations try to find one with the same address and flush it. error!( "Allocation 0x{:08X} with invalid ID {} was freed; this should never happen; you probably have an out-of-bounds write somewhere", address.get(), id ); - } if thread.is_dead() { + } + if thread.is_dead() { let mut zombie_events = thread.zombie_events().lock(); - zombie_events.push( - InternalEvent::Free { - timestamp, - id, - address, - backtrace, - tid: thread.system_tid() - } - ); + zombie_events.push(InternalEvent::Free { + timestamp, + id, + address, + backtrace, + tid: thread.system_tid(), + }); return; - } else if !thread.is_dead() && ENABLED.load( Ordering::Relaxed ) && !id.is_untracked() && !id.is_invalid() { - let bucket = - if id.thread == thread.unique_tid() { - let mut tracker = thread.allocation_tracker().0.lock(); - flush_pending( &mut tracker.buckets, timestamp ); + } else if !thread.is_dead() + && ENABLED.load(Ordering::Relaxed) + && !id.is_untracked() + && !id.is_invalid() + { + let bucket = if id.thread == thread.unique_tid() { + let mut tracker = thread.allocation_tracker().0.lock(); + flush_pending(&mut tracker.buckets, timestamp); - tracker.buckets.remove( &id.allocation ) - } else { - let registry = ALLOCATION_TRACKER_REGISTRY.read(); - if let Some( registry ) = registry.per_thread.get( &id.thread ) { - let mut tracker = registry.0.lock(); - flush_pending( &mut tracker.buckets, timestamp ); + tracker.buckets.remove(&id.allocation) + } else { + let registry = ALLOCATION_TRACKER_REGISTRY.read(); + if let Some(registry) = registry.per_thread.get(&id.thread) { + let mut tracker = registry.0.lock(); + flush_pending(&mut tracker.buckets, timestamp); - tracker.buckets.remove( &id.allocation ) - } else { - let mut allocations = registry.global.lock(); - flush_pending( &mut allocations, timestamp ); + tracker.buckets.remove(&id.allocation) + } else { + let mut allocations = registry.global.lock(); + flush_pending(&mut allocations, timestamp); - allocations.remove( &(id.thread, id.allocation) ) - } - }; + allocations.remove(&(id.thread, id.allocation)) + } + }; - if let Some( bucket ) = bucket { - if bucket.is_long_lived( timestamp ) { - crate::event::send_event_throttled_sharded( get_shard_key( bucket.id ), move || { - InternalEvent::AllocationBucket( bucket ) + if let Some(bucket) = bucket { + if bucket.is_long_lived(timestamp) { + crate::event::send_event_throttled_sharded(get_shard_key(bucket.id), move || { + InternalEvent::AllocationBucket(bucket) }); } else { return; @@ -337,13 +355,11 @@ pub fn on_free( } } - crate::event::send_event_throttled_sharded( get_shard_key( id ), move || { - InternalEvent::Free { - timestamp, - id, - address, - backtrace, - tid: thread.system_tid() - } + crate::event::send_event_throttled_sharded(get_shard_key(id), move || InternalEvent::Free { + timestamp, + id, + address, + backtrace, + tid: thread.system_tid(), }); } diff --git a/preload/src/api.rs b/preload/src/api.rs index 29902d79..497f5ec5 100644 --- a/preload/src/api.rs +++ b/preload/src/api.rs @@ -1,61 +1,56 @@ use std::mem; -use std::ptr; use std::num::NonZeroUsize; +use std::ptr; -use libc::{ - c_void, - c_int, - size_t, - off_t -}; +use libc::{c_int, c_void, off_t, size_t}; use common::event; use common::Timestamp; -use crate::InternalEvent; -use crate::event::{InternalAllocation, InternalAllocationId, send_event, send_event_throttled}; -use crate::global::{StrongThreadHandle, on_exit}; -use crate::smaps::{MapSource, MapKind}; +use crate::allocation_tracker::{on_allocation, on_free, on_reallocation}; +use crate::event::{send_event, send_event_throttled, InternalAllocation, InternalAllocationId}; +use crate::global::{on_exit, StrongThreadHandle}; use crate::opt; +use crate::smaps::{MapKind, MapSource}; use crate::syscall; use crate::timestamp::get_timestamp; use crate::unwind; -use crate::allocation_tracker::{on_allocation, on_reallocation, on_free}; +use crate::InternalEvent; extern "C" { #[link_name = "__libc_malloc"] - fn libc_malloc_real( size: size_t ) -> *mut c_void; + fn libc_malloc_real(size: size_t) -> *mut c_void; #[link_name = "__libc_calloc"] - fn libc_calloc_real( count: size_t, element_size: size_t ) -> *mut c_void; + fn libc_calloc_real(count: size_t, element_size: size_t) -> *mut c_void; #[link_name = "__libc_realloc"] - fn libc_realloc_real( ptr: *mut c_void, size: size_t ) -> *mut c_void; + fn libc_realloc_real(ptr: *mut c_void, size: size_t) -> *mut c_void; #[link_name = "__libc_free"] - fn libc_free_real( ptr: *mut c_void ); + fn libc_free_real(ptr: *mut c_void); #[link_name = "__libc_memalign"] - fn libc_memalign_real( alignment: size_t, size: size_t ) -> *mut c_void; + fn libc_memalign_real(alignment: size_t, size: size_t) -> *mut c_void; #[link_name = "__libc_mallopt"] - fn libc_mallopt_real( params: c_int, value: c_int ) -> c_int; + fn libc_mallopt_real(params: c_int, value: c_int) -> c_int; } +use tikv_jemalloc_sys::aligned_alloc as jem_aligned_alloc_real; +use tikv_jemalloc_sys::calloc as jem_calloc_real; +use tikv_jemalloc_sys::free as jem_free_real; +use tikv_jemalloc_sys::is_initialized as jem_is_initialized; +use tikv_jemalloc_sys::mallctlbymib as jem_mallctlbymib_real; +use tikv_jemalloc_sys::mallctlnametomib as jem_mallctlnametomib_real; use tikv_jemalloc_sys::malloc as jem_malloc_real; +use tikv_jemalloc_sys::malloc_stats_print as jem_malloc_stats_print_real; +use tikv_jemalloc_sys::malloc_usable_size as jem_malloc_usable_size_real; use tikv_jemalloc_sys::mallocx as jem_mallocx_real; -use tikv_jemalloc_sys::calloc as jem_calloc_real; -use tikv_jemalloc_sys::sdallocx as jem_sdallocx_real; -use tikv_jemalloc_sys::realloc as jem_realloc_real; +use tikv_jemalloc_sys::nallocx as jem_nallocx_real; use tikv_jemalloc_sys::rallocx as jem_rallocx_real; +use tikv_jemalloc_sys::realloc as jem_realloc_real; +use tikv_jemalloc_sys::sdallocx as jem_sdallocx_real; use tikv_jemalloc_sys::xallocx as jem_xallocx_real; -use tikv_jemalloc_sys::nallocx as jem_nallocx_real; -use tikv_jemalloc_sys::malloc_usable_size as jem_malloc_usable_size_real; -use tikv_jemalloc_sys::mallctlnametomib as jem_mallctlnametomib_real; -use tikv_jemalloc_sys::mallctlbymib as jem_mallctlbymib_real; -use tikv_jemalloc_sys::malloc_stats_print as jem_malloc_stats_print_real; -use tikv_jemalloc_sys::free as jem_free_real; -use tikv_jemalloc_sys::is_initialized as jem_is_initialized; -use tikv_jemalloc_sys::aligned_alloc as jem_aligned_alloc_real; extern "C" { #[link_name = "_rjem_mp_memalign"] - fn jem_memalign_real( alignment: size_t, size: size_t ) -> *mut c_void; + fn jem_memalign_real(alignment: size_t, size: size_t) -> *mut c_void; } extern "C" { @@ -64,49 +59,70 @@ extern "C" { } #[no_mangle] -pub unsafe extern "C" fn bytehound_jemalloc_raw_mmap( addr: *mut c_void, length: size_t, prot: c_int, flags: c_int, fildes: c_int, off: off_t ) -> *mut c_void { +pub unsafe extern "C" fn bytehound_jemalloc_raw_mmap( + addr: *mut c_void, + length: size_t, + prot: c_int, + flags: c_int, + fildes: c_int, + off: off_t, +) -> *mut c_void { if !jem_is_initialized() { let id = crate::global::next_map_id(); let mut maps_registry = crate::global::MMAP_REGISTRY.lock().unwrap(); - let ptr = syscall::mmap( addr, length, prot, flags, fildes, off ); + let ptr = syscall::mmap(addr, length, prot, flags, fildes, off); if ptr != libc::MAP_FAILED { - maps_registry.set_vma_name( ptr, length, id, MapKind::Jemalloc ); + maps_registry.set_vma_name(ptr, length, id, MapKind::Jemalloc); } return ptr; } - mmap_internal( addr, length, prot, flags, fildes, off, MapKind::Jemalloc ) + mmap_internal(addr, length, prot, flags, fildes, off, MapKind::Jemalloc) } #[no_mangle] -pub unsafe extern "C" fn bytehound_jemalloc_raw_munmap( addr: *mut c_void, length: size_t ) -> c_int { +pub unsafe extern "C" fn bytehound_jemalloc_raw_munmap(addr: *mut c_void, length: size_t) -> c_int { if !jem_is_initialized() { let mut maps_registry = crate::global::MMAP_REGISTRY.lock().unwrap(); - let result = syscall::munmap( addr, length ); - maps_registry.clear_vma_name( addr, length ); + let result = syscall::munmap(addr, length); + maps_registry.clear_vma_name(addr, length); return result; } - munmap( addr, length ) + munmap(addr, length) } #[no_mangle] -pub unsafe extern "C" fn bytehound_mimalloc_raw_mmap( addr: *mut c_void, length: size_t, prot: c_int, flags: c_int, mut fildes: c_int, off: off_t ) -> *mut c_void { - assert_eq!( fildes, -1 ); - assert_eq!( off, 0 ); +pub unsafe extern "C" fn bytehound_mimalloc_raw_mmap( + addr: *mut c_void, + length: size_t, + prot: c_int, + flags: c_int, + mut fildes: c_int, + off: off_t, +) -> *mut c_void { + assert_eq!(fildes, -1); + assert_eq!(off, 0); let id = crate::global::next_map_id(); - let name = crate::smaps::construct_name( id, "bytehound" ); + let name = crate::smaps::construct_name(id, "bytehound"); if crate::global::is_pr_set_vma_anon_name_supported() { - if let Ok( lock ) = crate::global::MMAP_REGISTRY.try_lock() { - let pointer = syscall::mmap( addr, length, prot | libc::PROT_READ | libc::PROT_WRITE, flags, fildes, off ); + if let Ok(lock) = crate::global::MMAP_REGISTRY.try_lock() { + let pointer = syscall::mmap( + addr, + length, + prot | libc::PROT_READ | libc::PROT_WRITE, + flags, + fildes, + off, + ); if pointer != libc::MAP_FAILED { - crate::syscall::pr_set_vma_anon_name( pointer, length, &name ); + crate::syscall::pr_set_vma_anon_name(pointer, length, &name); } - std::mem::drop( lock ); + std::mem::drop(lock); return pointer; } } @@ -114,56 +130,70 @@ pub unsafe extern "C" fn bytehound_mimalloc_raw_mmap( addr: *mut c_void, length: // Here we abuse the fact that an memfd-based map will have its name displayed in maps. // // It's nasty, but it works. - let fd = libc::memfd_create( name.as_ptr().cast(), libc::MFD_CLOEXEC ); - assert!( fd >= 0 ); - libc::ftruncate( fd, length as _ ); + let fd = libc::memfd_create(name.as_ptr().cast(), libc::MFD_CLOEXEC); + assert!(fd >= 0); + libc::ftruncate(fd, length as _); fildes = fd; - let pointer = syscall::mmap( addr, length, prot | libc::PROT_READ | libc::PROT_WRITE, flags & (!libc::MAP_ANONYMOUS) | libc::MAP_SHARED, fildes, off ); - libc::close( fildes ); + let pointer = syscall::mmap( + addr, + length, + prot | libc::PROT_READ | libc::PROT_WRITE, + flags & (!libc::MAP_ANONYMOUS) | libc::MAP_SHARED, + fildes, + off, + ); + libc::close(fildes); pointer } #[no_mangle] -pub unsafe extern "C" fn bytehound_mimalloc_raw_munmap( addr: *mut c_void, length: size_t ) -> c_int { +pub unsafe extern "C" fn bytehound_mimalloc_raw_munmap(addr: *mut c_void, length: size_t) -> c_int { let _lock = crate::global::MMAP_REGISTRY.try_lock(); - syscall::munmap( addr, length ) + syscall::munmap(addr, length) } #[no_mangle] -pub unsafe extern "C" fn bytehound_mimalloc_raw_mprotect( _addr: *mut c_void, _length: size_t, _prot: c_int ) -> c_int { +pub unsafe extern "C" fn bytehound_mimalloc_raw_mprotect( + _addr: *mut c_void, + _length: size_t, + _prot: c_int, +) -> c_int { 0 } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn _exit( status: c_int ) { +pub unsafe extern "C" fn _exit(status: c_int) { on_exit(); - syscall::exit( status as u32 ); + syscall::exit(status as u32); } #[allow(non_snake_case)] #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn _Exit( status: c_int ) { - _exit( status ); +pub unsafe extern "C" fn _Exit(status: c_int) { + _exit(status); } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn malloc_usable_size( ptr: *mut c_void ) -> size_t { +pub unsafe extern "C" fn malloc_usable_size(ptr: *mut c_void) -> size_t { if ptr.is_null() { return 0; } #[cfg(feature = "jemalloc")] { - _rjem_malloc_usable_size( ptr ) + _rjem_malloc_usable_size(ptr) } #[cfg(not(feature = "jemalloc"))] { - let usable_size = get_allocation_metadata( ptr ).usable_size; - match usable_size.checked_sub( mem::size_of::< InternalAllocationId >() ) { - Some( size ) => size, - None => panic!( "malloc_usable_size: underflow (pointer=0x{:016X}, usable_size={})", ptr as usize , usable_size ) + let usable_size = get_allocation_metadata(ptr).usable_size; + match usable_size.checked_sub(mem::size_of::()) { + Some(size) => size, + None => panic!( + "malloc_usable_size: underflow (pointer=0x{:016X}, usable_size={})", + ptr as usize, usable_size + ), } } } @@ -171,109 +201,104 @@ pub unsafe extern "C" fn malloc_usable_size( ptr: *mut c_void ) -> size_t { #[derive(Debug)] struct Metadata { flags: u32, - usable_size: usize + usable_size: usize, } -fn get_allocation_metadata( ptr: *mut c_void ) -> Metadata { +fn get_allocation_metadata(ptr: *mut c_void) -> Metadata { if crate::global::using_unprefixed_jemalloc() { return Metadata { flags: 0, - usable_size: unsafe { jem_malloc_usable_size_real( ptr ) } - } + usable_size: unsafe { jem_malloc_usable_size_real(ptr) }, + }; } else { - let raw_chunk_size = unsafe { *(ptr as *mut usize).offset( -1 ) }; + let raw_chunk_size = unsafe { *(ptr as *mut usize).offset(-1) }; let flags = raw_chunk_size & 0b111; let chunk_size = raw_chunk_size & !0b111; let is_mmapped = flags & 2 != 0; - let usable_size = chunk_size - mem::size_of::< usize >() * if is_mmapped { 2 } else { 1 }; + let usable_size = chunk_size - mem::size_of::() * if is_mmapped { 2 } else { 1 }; Metadata { flags: flags as u32, - usable_size + usable_size, } } } -unsafe fn tracking_pointer( pointer: *mut c_void, usable_size: usize ) -> *mut InternalAllocationId { - let tracking_offset = usable_size - mem::size_of::< InternalAllocationId >(); - (pointer as *mut u8).add( tracking_offset ) as *mut InternalAllocationId +unsafe fn tracking_pointer(pointer: *mut c_void, usable_size: usize) -> *mut InternalAllocationId { + let tracking_offset = usable_size - mem::size_of::(); + (pointer as *mut u8).add(tracking_offset) as *mut InternalAllocationId } enum AllocationKind { Malloc, Calloc, - Aligned( size_t ) + Aligned(size_t), } #[inline(always)] -unsafe fn allocate( requested_size: usize, kind: AllocationKind ) -> *mut c_void { - let effective_size = match requested_size.checked_add( mem::size_of::< InternalAllocationId >() ) { - Some( size ) => size, - None => return ptr::null_mut() +unsafe fn allocate(requested_size: usize, kind: AllocationKind) -> *mut c_void { + let effective_size = match requested_size.checked_add(mem::size_of::()) { + Some(size) => size, + None => return ptr::null_mut(), }; let mut thread = StrongThreadHandle::acquire(); - let pointer = - if !crate::global::using_unprefixed_jemalloc() { - match kind { - AllocationKind::Malloc => { - if opt::get().zero_memory { - libc_calloc_real( effective_size as size_t, 1 ) - } else { - libc_malloc_real( effective_size as size_t ) - } - }, - AllocationKind::Calloc => { - libc_calloc_real( effective_size as size_t, 1 ) - }, - AllocationKind::Aligned( alignment ) => { - libc_memalign_real( alignment, effective_size as size_t ) + let pointer = if !crate::global::using_unprefixed_jemalloc() { + match kind { + AllocationKind::Malloc => { + if opt::get().zero_memory { + libc_calloc_real(effective_size as size_t, 1) + } else { + libc_malloc_real(effective_size as size_t) } } - } else { - match kind { - AllocationKind::Malloc => { - if opt::get().zero_memory { - jem_calloc_real( effective_size as size_t, 1 ) - } else { - jem_malloc_real( effective_size as size_t ) - } - }, - AllocationKind::Calloc => { - jem_calloc_real( effective_size as size_t, 1 ) - }, - AllocationKind::Aligned( alignment ) => { - jem_memalign_real( alignment, effective_size as size_t ) + AllocationKind::Calloc => libc_calloc_real(effective_size as size_t, 1), + AllocationKind::Aligned(alignment) => { + libc_memalign_real(alignment, effective_size as size_t) + } + } + } else { + match kind { + AllocationKind::Malloc => { + if opt::get().zero_memory { + jem_calloc_real(effective_size as size_t, 1) + } else { + jem_malloc_real(effective_size as size_t) } } - }; + AllocationKind::Calloc => jem_calloc_real(effective_size as size_t, 1), + AllocationKind::Aligned(alignment) => { + jem_memalign_real(alignment, effective_size as size_t) + } + } + }; if !crate::global::is_actively_running() { thread = None; } - let address = match NonZeroUsize::new( pointer as usize ) { - Some( address ) => address, - None => return pointer + let address = match NonZeroUsize::new(pointer as usize) { + Some(address) => address, + None => return pointer, }; - let mut metadata = get_allocation_metadata( pointer ); - let tracking_pointer = tracking_pointer( pointer, metadata.usable_size ); + let mut metadata = get_allocation_metadata(pointer); + let tracking_pointer = tracking_pointer(pointer, metadata.usable_size); - let mut thread = if let Some( thread ) = thread { + let mut thread = if let Some(thread) = thread { thread } else { - std::ptr::write_unaligned( tracking_pointer, InternalAllocationId::UNTRACKED ); + std::ptr::write_unaligned(tracking_pointer, InternalAllocationId::UNTRACKED); return pointer; }; let id = thread.on_new_allocation(); - std::ptr::write_unaligned( tracking_pointer, id ); + std::ptr::write_unaligned(tracking_pointer, id); - let backtrace = unwind::grab( &mut thread ); + let backtrace = unwind::grab(&mut thread); - if matches!( kind, AllocationKind::Calloc ) { + if matches!(kind, AllocationKind::Calloc) { metadata.flags |= event::ALLOC_FLAG_CALLOC; } @@ -285,77 +310,77 @@ unsafe fn allocate( requested_size: usize, kind: AllocationKind ) -> *mut c_void extra_usable_space: (metadata.usable_size - requested_size) as u32, }; - on_allocation( id, allocation, backtrace, thread ); + on_allocation(id, allocation, backtrace, thread); pointer } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn malloc( size: size_t ) -> *mut c_void { - allocate( size, AllocationKind::Malloc ) +pub unsafe extern "C" fn malloc(size: size_t) -> *mut c_void { + allocate(size, AllocationKind::Malloc) } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn calloc( count: size_t, element_size: size_t ) -> *mut c_void { - let size = match count.checked_mul( element_size ) { +pub unsafe extern "C" fn calloc(count: size_t, element_size: size_t) -> *mut c_void { + let size = match count.checked_mul(element_size) { None => return ptr::null_mut(), - Some( size ) => size + Some(size) => size, }; - allocate( size, AllocationKind::Calloc ) + allocate(size, AllocationKind::Calloc) } #[inline(always)] -unsafe fn realloc_impl( old_pointer: *mut c_void, requested_size: size_t ) -> *mut c_void { - let old_address = match NonZeroUsize::new( old_pointer as usize ) { - Some( old_address ) => old_address, - None => return malloc( requested_size ) +unsafe fn realloc_impl(old_pointer: *mut c_void, requested_size: size_t) -> *mut c_void { + let old_address = match NonZeroUsize::new(old_pointer as usize) { + Some(old_address) => old_address, + None => return malloc(requested_size), }; if requested_size == 0 { - free( old_pointer ); + free(old_pointer); return ptr::null_mut(); } - let effective_size = match requested_size.checked_add( mem::size_of::< InternalAllocationId >() ) { - Some( size ) => size, - None => return ptr::null_mut() + let effective_size = match requested_size.checked_add(mem::size_of::()) { + Some(size) => size, + None => return ptr::null_mut(), }; - let old_metadata = get_allocation_metadata( old_pointer ); - let old_tracking_pointer = tracking_pointer( old_pointer, old_metadata.usable_size ); - let id = std::ptr::read_unaligned( old_tracking_pointer ); - debug_assert!( id.is_valid() ); + let old_metadata = get_allocation_metadata(old_pointer); + let old_tracking_pointer = tracking_pointer(old_pointer, old_metadata.usable_size); + let id = std::ptr::read_unaligned(old_tracking_pointer); + debug_assert!(id.is_valid()); let mut thread = StrongThreadHandle::acquire(); let new_pointer = if !crate::global::using_unprefixed_jemalloc() { - libc_realloc_real( old_pointer, effective_size ) + libc_realloc_real(old_pointer, effective_size) } else { - jem_realloc_real( old_pointer, effective_size ) + jem_realloc_real(old_pointer, effective_size) }; if id.is_untracked() && !crate::global::is_actively_running() { thread = None; } - let mut thread = if let Some( thread ) = thread { + let mut thread = if let Some(thread) = thread { thread } else { if new_pointer.is_null() { return ptr::null_mut(); } else { - let new_metadata = get_allocation_metadata( new_pointer ); - let new_tracking_pointer = tracking_pointer( new_pointer, new_metadata.usable_size ); - std::ptr::write_unaligned( new_tracking_pointer, InternalAllocationId::UNTRACKED ); + let new_metadata = get_allocation_metadata(new_pointer); + let new_tracking_pointer = tracking_pointer(new_pointer, new_metadata.usable_size); + std::ptr::write_unaligned(new_tracking_pointer, InternalAllocationId::UNTRACKED); return new_pointer; } }; - let backtrace = unwind::grab( &mut thread ); + let backtrace = unwind::grab(&mut thread); - if let Some( new_address ) = NonZeroUsize::new( new_pointer as usize ) { - let new_metadata = get_allocation_metadata( new_pointer ); - let new_tracking_pointer = tracking_pointer( new_pointer, new_metadata.usable_size ); - std::ptr::write_unaligned( new_tracking_pointer, id ); + if let Some(new_address) = NonZeroUsize::new(new_pointer as usize) { + let new_metadata = get_allocation_metadata(new_pointer); + let new_tracking_pointer = tracking_pointer(new_pointer, new_metadata.usable_size); + std::ptr::write_unaligned(new_tracking_pointer, id); let allocation = InternalAllocation { address: new_address, @@ -365,82 +390,94 @@ unsafe fn realloc_impl( old_pointer: *mut c_void, requested_size: size_t ) -> *m extra_usable_space: (new_metadata.usable_size - requested_size) as u32, }; - on_reallocation( id, old_address, allocation, backtrace, thread ); + on_reallocation(id, old_address, allocation, backtrace, thread); new_pointer } else { - on_free( id, old_address, Some( backtrace ), thread ); + on_free(id, old_address, Some(backtrace), thread); ptr::null_mut() } } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn realloc( old_ptr: *mut c_void, size: size_t ) -> *mut c_void { - realloc_impl( old_ptr, size ) +pub unsafe extern "C" fn realloc(old_ptr: *mut c_void, size: size_t) -> *mut c_void { + realloc_impl(old_ptr, size) } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn reallocarray( old_ptr: *mut c_void, count: size_t, element_size: size_t ) -> *mut c_void { - let size = match (count as usize).checked_mul( element_size as usize ) { +pub unsafe extern "C" fn reallocarray( + old_ptr: *mut c_void, + count: size_t, + element_size: size_t, +) -> *mut c_void { + let size = match (count as usize).checked_mul(element_size as usize) { None => { *libc::__errno_location() = libc::ENOMEM; - return ptr::null_mut() - }, - Some( size ) => size as size_t + return ptr::null_mut(); + } + Some(size) => size as size_t, }; - realloc_impl( old_ptr, size ) + realloc_impl(old_ptr, size) } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn free( pointer: *mut c_void ) { - let address = match NonZeroUsize::new( pointer as usize ) { - Some( address ) => address, - None => return +pub unsafe extern "C" fn free(pointer: *mut c_void) { + let address = match NonZeroUsize::new(pointer as usize) { + Some(address) => address, + None => return, }; - let metadata = get_allocation_metadata( pointer ); - let tracking_pointer = tracking_pointer( pointer, metadata.usable_size ); - let id = std::ptr::read_unaligned( tracking_pointer ); - debug_assert!( id.is_valid() ); + let metadata = get_allocation_metadata(pointer); + let tracking_pointer = tracking_pointer(pointer, metadata.usable_size); + let id = std::ptr::read_unaligned(tracking_pointer); + debug_assert!(id.is_valid()); let mut thread = StrongThreadHandle::acquire(); if !crate::global::using_unprefixed_jemalloc() { - libc_free_real( pointer ); + libc_free_real(pointer); } else { - jem_free_real( pointer ); + jem_free_real(pointer); } if id.is_untracked() && !crate::global::is_actively_running() { thread = None; } - let mut thread = if let Some( thread ) = thread { thread } else { return }; + let mut thread = if let Some(thread) = thread { + thread + } else { + return; + }; let backtrace = if opt::get().grab_backtraces_on_free { - Some( unwind::grab( &mut thread ) ) + Some(unwind::grab(&mut thread)) } else { None }; - on_free( id, address, backtrace, thread ); + on_free(id, address, backtrace, thread); } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn _rjem_malloc( requested_size: size_t ) -> *mut c_void { - let pointer = jemalloc_allocate( requested_size, JeAllocationKind::Malloc ); +pub unsafe extern "C" fn _rjem_malloc(requested_size: size_t) -> *mut c_void { + let pointer = jemalloc_allocate(requested_size, JeAllocationKind::Malloc); #[cfg(feature = "debug-logs")] - trace!( "_rjem_malloc: requested_size={} -> 0x{:X}", requested_size, pointer as usize ); + trace!( + "_rjem_malloc: requested_size={} -> 0x{:X}", + requested_size, + pointer as usize + ); pointer } enum JeAllocationKind { Malloc, - MallocX( c_int ), + MallocX(c_int), Calloc, - Memalign( size_t ), - AlignedAlloc( size_t ) + Memalign(size_t), + AlignedAlloc(size_t), } -fn translate_jemalloc_flags( flags: c_int ) -> u32 { +fn translate_jemalloc_flags(flags: c_int) -> u32 { const MALLOCX_ZERO: c_int = 0x40; let mut internal_flags = event::ALLOC_FLAG_JEMALLOC; @@ -451,45 +488,57 @@ fn translate_jemalloc_flags( flags: c_int ) -> u32 { internal_flags } -unsafe fn jemalloc_allocate( requested_size: usize, kind: JeAllocationKind ) -> *mut c_void { - let effective_size = match requested_size.checked_add( mem::size_of::< InternalAllocationId >() ) { - Some( size ) => size, - None => return ptr::null_mut() +unsafe fn jemalloc_allocate(requested_size: usize, kind: JeAllocationKind) -> *mut c_void { + let effective_size = match requested_size.checked_add(mem::size_of::()) { + Some(size) => size, + None => return ptr::null_mut(), }; let mut thread = StrongThreadHandle::acquire(); let (pointer, flags) = match kind { - JeAllocationKind::Malloc => (jem_malloc_real( effective_size ), event::ALLOC_FLAG_JEMALLOC), - JeAllocationKind::MallocX( flags ) => (jem_mallocx_real( effective_size, flags ), translate_jemalloc_flags( flags )), - JeAllocationKind::Calloc => (jem_calloc_real( 1, effective_size ), event::ALLOC_FLAG_JEMALLOC | event::ALLOC_FLAG_CALLOC), - JeAllocationKind::Memalign( alignment ) => (jem_memalign_real( alignment, effective_size as size_t ), event::ALLOC_FLAG_JEMALLOC), - JeAllocationKind::AlignedAlloc( alignment ) => (jem_aligned_alloc_real( alignment, effective_size as size_t ), event::ALLOC_FLAG_JEMALLOC) + JeAllocationKind::Malloc => (jem_malloc_real(effective_size), event::ALLOC_FLAG_JEMALLOC), + JeAllocationKind::MallocX(flags) => ( + jem_mallocx_real(effective_size, flags), + translate_jemalloc_flags(flags), + ), + JeAllocationKind::Calloc => ( + jem_calloc_real(1, effective_size), + event::ALLOC_FLAG_JEMALLOC | event::ALLOC_FLAG_CALLOC, + ), + JeAllocationKind::Memalign(alignment) => ( + jem_memalign_real(alignment, effective_size as size_t), + event::ALLOC_FLAG_JEMALLOC, + ), + JeAllocationKind::AlignedAlloc(alignment) => ( + jem_aligned_alloc_real(alignment, effective_size as size_t), + event::ALLOC_FLAG_JEMALLOC, + ), }; if !crate::global::is_actively_running() { thread = None; } - let address = match NonZeroUsize::new( pointer as usize ) { - Some( address ) => address, - None => return pointer + let address = match NonZeroUsize::new(pointer as usize) { + Some(address) => address, + None => return pointer, }; - let usable_size = jem_malloc_usable_size_real( pointer ); - debug_assert!( usable_size >= effective_size ); - let tracking_pointer = tracking_pointer( pointer, usable_size ); + let usable_size = jem_malloc_usable_size_real(pointer); + debug_assert!(usable_size >= effective_size); + let tracking_pointer = tracking_pointer(pointer, usable_size); - let mut thread = if let Some( thread ) = thread { + let mut thread = if let Some(thread) = thread { thread } else { - std::ptr::write_unaligned( tracking_pointer, InternalAllocationId::UNTRACKED ); + std::ptr::write_unaligned(tracking_pointer, InternalAllocationId::UNTRACKED); return pointer; }; let id = thread.on_new_allocation(); - std::ptr::write_unaligned( tracking_pointer, id ); + std::ptr::write_unaligned(tracking_pointer, id); - let backtrace = unwind::grab( &mut thread ); + let backtrace = unwind::grab(&mut thread); let allocation = InternalAllocation { address, size: requested_size as usize, @@ -498,139 +547,193 @@ unsafe fn jemalloc_allocate( requested_size: usize, kind: JeAllocationKind ) -> extra_usable_space: 0, }; - on_allocation( id, allocation, backtrace, thread ); + on_allocation(id, allocation, backtrace, thread); pointer } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn _rjem_mallocx( requested_size: size_t, flags: c_int ) -> *mut c_void { - let pointer = jemalloc_allocate( requested_size, JeAllocationKind::MallocX( flags ) ); +pub unsafe extern "C" fn _rjem_mallocx(requested_size: size_t, flags: c_int) -> *mut c_void { + let pointer = jemalloc_allocate(requested_size, JeAllocationKind::MallocX(flags)); #[cfg(feature = "debug-logs")] - trace!( "_rjem_mallocx: requested_size={} flags={} -> 0x{:X}", requested_size, flags, pointer as usize ); + trace!( + "_rjem_mallocx: requested_size={} flags={} -> 0x{:X}", + requested_size, + flags, + pointer as usize + ); pointer } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn _rjem_calloc( count: size_t, element_size: size_t ) -> *mut c_void { - let requested_size = match count.checked_mul( element_size ) { +pub unsafe extern "C" fn _rjem_calloc(count: size_t, element_size: size_t) -> *mut c_void { + let requested_size = match count.checked_mul(element_size) { None => return ptr::null_mut(), - Some( size ) => size + Some(size) => size, }; - let pointer = jemalloc_allocate( requested_size, JeAllocationKind::Calloc ); + let pointer = jemalloc_allocate(requested_size, JeAllocationKind::Calloc); #[cfg(feature = "debug-logs")] - trace!( "_rjem_calloc: requested_size={} -> 0x{:X}", requested_size, pointer as usize ); + trace!( + "_rjem_calloc: requested_size={} -> 0x{:X}", + requested_size, + pointer as usize + ); pointer } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn _rjem_sdallocx( pointer: *mut c_void, requested_size: size_t, flags: c_int ) { +pub unsafe extern "C" fn _rjem_sdallocx( + pointer: *mut c_void, + requested_size: size_t, + flags: c_int, +) { #[cfg(feature = "debug-logs")] - trace!( "_rjem_sdallocx: pointer=0x{:X} requested_size={} flags={}", pointer as usize, requested_size, flags ); - - let address = match NonZeroUsize::new( pointer as usize ) { - Some( address ) => address, - None => return + trace!( + "_rjem_sdallocx: pointer=0x{:X} requested_size={} flags={}", + pointer as usize, + requested_size, + flags + ); + + let address = match NonZeroUsize::new(pointer as usize) { + Some(address) => address, + None => return, }; - let effective_size = match requested_size.checked_add( mem::size_of::< InternalAllocationId >() ) { - Some( size ) => size, - None => return + let effective_size = match requested_size.checked_add(mem::size_of::()) { + Some(size) => size, + None => return, }; - let usable_size = jem_malloc_usable_size_real( pointer ); - debug_assert!( usable_size >= effective_size, "tried to deallocate an allocation without space for the tracking pointer: 0x{:X}", pointer as usize ); - let tracking_pointer = tracking_pointer( pointer, usable_size ); - let id = std::ptr::read_unaligned( tracking_pointer ); - debug_assert!( id.is_valid() ); + let usable_size = jem_malloc_usable_size_real(pointer); + debug_assert!( + usable_size >= effective_size, + "tried to deallocate an allocation without space for the tracking pointer: 0x{:X}", + pointer as usize + ); + let tracking_pointer = tracking_pointer(pointer, usable_size); + let id = std::ptr::read_unaligned(tracking_pointer); + debug_assert!(id.is_valid()); let mut thread = StrongThreadHandle::acquire(); - jem_sdallocx_real( pointer, effective_size, flags ); + jem_sdallocx_real(pointer, effective_size, flags); if id.is_untracked() && !crate::global::is_actively_running() { thread = None; } - let mut thread = if let Some( thread ) = thread { thread } else { return }; - let backtrace = - if opt::get().grab_backtraces_on_free { - Some( unwind::grab( &mut thread ) ) - } else { - None - }; + let mut thread = if let Some(thread) = thread { + thread + } else { + return; + }; + let backtrace = if opt::get().grab_backtraces_on_free { + Some(unwind::grab(&mut thread)) + } else { + None + }; - on_free( id, address, backtrace, thread ); + on_free(id, address, backtrace, thread); } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn _rjem_realloc( old_pointer: *mut c_void, requested_size: size_t ) -> *mut c_void { - let new_pointer = jemalloc_reallocate( old_pointer, requested_size, None ); +pub unsafe extern "C" fn _rjem_realloc( + old_pointer: *mut c_void, + requested_size: size_t, +) -> *mut c_void { + let new_pointer = jemalloc_reallocate(old_pointer, requested_size, None); #[cfg(feature = "debug-logs")] - trace!( "_rjem_realloc: old_pointer=0x{:X} requested_size={} -> 0x{:X}", old_pointer as usize, requested_size, new_pointer as usize ); + trace!( + "_rjem_realloc: old_pointer=0x{:X} requested_size={} -> 0x{:X}", + old_pointer as usize, + requested_size, + new_pointer as usize + ); new_pointer } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn _rjem_rallocx( old_pointer: *mut c_void, requested_size: size_t, flags: c_int ) -> *mut c_void { - let new_pointer = jemalloc_reallocate( old_pointer, requested_size, Some( flags ) ); +pub unsafe extern "C" fn _rjem_rallocx( + old_pointer: *mut c_void, + requested_size: size_t, + flags: c_int, +) -> *mut c_void { + let new_pointer = jemalloc_reallocate(old_pointer, requested_size, Some(flags)); #[cfg(feature = "debug-logs")] - trace!( "_rjem_rallocx: old_pointer=0x{:X} requested_size={} flags={} -> 0x{:X}", old_pointer as usize, requested_size, flags, new_pointer as usize ); + trace!( + "_rjem_rallocx: old_pointer=0x{:X} requested_size={} flags={} -> 0x{:X}", + old_pointer as usize, + requested_size, + flags, + new_pointer as usize + ); new_pointer } -unsafe fn jemalloc_reallocate( old_pointer: *mut c_void, requested_size: size_t, flags: Option< c_int > ) -> *mut c_void { - let old_address = match NonZeroUsize::new( old_pointer as usize ) { - Some( old_address ) => old_address, - None => return - if let Some( flags ) = flags { - _rjem_mallocx( requested_size, flags ) +unsafe fn jemalloc_reallocate( + old_pointer: *mut c_void, + requested_size: size_t, + flags: Option, +) -> *mut c_void { + let old_address = match NonZeroUsize::new(old_pointer as usize) { + Some(old_address) => old_address, + None => { + return if let Some(flags) = flags { + _rjem_mallocx(requested_size, flags) } else { - _rjem_malloc( requested_size ) + _rjem_malloc(requested_size) } + } }; - let effective_size = match requested_size.checked_add( mem::size_of::< InternalAllocationId >() ) { - Some( size ) => size, - None => return ptr::null_mut() + let effective_size = match requested_size.checked_add(mem::size_of::()) { + Some(size) => size, + None => return ptr::null_mut(), }; - let old_usable_size = jem_malloc_usable_size_real( old_pointer ); - let old_tracking_pointer = tracking_pointer( old_pointer, old_usable_size ); - let id = std::ptr::read_unaligned( old_tracking_pointer ); - debug_assert!( id.is_valid() ); + let old_usable_size = jem_malloc_usable_size_real(old_pointer); + let old_tracking_pointer = tracking_pointer(old_pointer, old_usable_size); + let id = std::ptr::read_unaligned(old_tracking_pointer); + debug_assert!(id.is_valid()); let mut thread = StrongThreadHandle::acquire(); - let (new_pointer, flags) = if let Some( flags ) = flags { - (jem_rallocx_real( old_pointer, effective_size, flags ), translate_jemalloc_flags( flags )) + let (new_pointer, flags) = if let Some(flags) = flags { + ( + jem_rallocx_real(old_pointer, effective_size, flags), + translate_jemalloc_flags(flags), + ) } else { - (jem_realloc_real( old_pointer, effective_size ), translate_jemalloc_flags( 0 )) + ( + jem_realloc_real(old_pointer, effective_size), + translate_jemalloc_flags(0), + ) }; if id.is_untracked() && !crate::global::is_actively_running() { thread = None; } - let mut thread = if let Some( thread ) = thread { + let mut thread = if let Some(thread) = thread { thread } else { if new_pointer.is_null() { return ptr::null_mut(); } else { - let new_usable_size = jem_malloc_usable_size_real( new_pointer ); - debug_assert!( new_usable_size >= effective_size ); - let new_tracking_pointer = tracking_pointer( new_pointer, new_usable_size ); - std::ptr::write_unaligned( new_tracking_pointer, InternalAllocationId::UNTRACKED ); + let new_usable_size = jem_malloc_usable_size_real(new_pointer); + debug_assert!(new_usable_size >= effective_size); + let new_tracking_pointer = tracking_pointer(new_pointer, new_usable_size); + std::ptr::write_unaligned(new_tracking_pointer, InternalAllocationId::UNTRACKED); return new_pointer; } }; - let backtrace = unwind::grab( &mut thread ); + let backtrace = unwind::grab(&mut thread); - if let Some( new_address ) = NonZeroUsize::new( new_pointer as usize ) { - let new_usable_size = jem_malloc_usable_size_real( new_pointer ); - debug_assert!( new_usable_size >= effective_size ); - let new_tracking_pointer = tracking_pointer( new_pointer, new_usable_size ); - std::ptr::write_unaligned( new_tracking_pointer, id ); + if let Some(new_address) = NonZeroUsize::new(new_pointer as usize) { + let new_usable_size = jem_malloc_usable_size_real(new_pointer); + debug_assert!(new_usable_size >= effective_size); + let new_tracking_pointer = tracking_pointer(new_pointer, new_usable_size); + std::ptr::write_unaligned(new_tracking_pointer, id); let allocation = InternalAllocation { address: new_address, @@ -640,109 +743,136 @@ unsafe fn jemalloc_reallocate( old_pointer: *mut c_void, requested_size: size_t, extra_usable_space: 0, }; - on_reallocation( id, old_address, allocation, backtrace, thread ); + on_reallocation(id, old_address, allocation, backtrace, thread); new_pointer } else { - on_free( id, old_address, Some( backtrace ), thread ); + on_free(id, old_address, Some(backtrace), thread); ptr::null_mut() } } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn _rjem_xallocx( pointer: *mut c_void, requested_size: size_t, extra: size_t, flags: c_int ) -> size_t { - let address = match NonZeroUsize::new( pointer as usize ) { - Some( address ) => address, - None => return 0 +pub unsafe extern "C" fn _rjem_xallocx( + pointer: *mut c_void, + requested_size: size_t, + extra: size_t, + flags: c_int, +) -> size_t { + let address = match NonZeroUsize::new(pointer as usize) { + Some(address) => address, + None => return 0, }; - let effective_size = match requested_size.checked_add( mem::size_of::< InternalAllocationId >() ) { - Some( size ) => size, - None => return _rjem_malloc_usable_size( pointer ) + let effective_size = match requested_size.checked_add(mem::size_of::()) { + Some(size) => size, + None => return _rjem_malloc_usable_size(pointer), }; - let old_usable_size = jem_malloc_usable_size_real( pointer ); - let old_tracking_pointer = tracking_pointer( pointer, old_usable_size ); - let id = std::ptr::read_unaligned( old_tracking_pointer ); - debug_assert!( id.is_valid() ); + let old_usable_size = jem_malloc_usable_size_real(pointer); + let old_tracking_pointer = tracking_pointer(pointer, old_usable_size); + let id = std::ptr::read_unaligned(old_tracking_pointer); + debug_assert!(id.is_valid()); let mut thread = StrongThreadHandle::acquire(); - let new_effective_size = jem_xallocx_real( pointer, effective_size, extra, flags ); - let new_requested_size = new_effective_size.checked_sub( mem::size_of::< InternalAllocationId >() ).expect( "_rjem_xallocx: underflow" ); + let new_effective_size = jem_xallocx_real(pointer, effective_size, extra, flags); + let new_requested_size = new_effective_size + .checked_sub(mem::size_of::()) + .expect("_rjem_xallocx: underflow"); if id.is_untracked() && !crate::global::is_actively_running() { thread = None; } - let mut thread = if let Some( thread ) = thread { + let mut thread = if let Some(thread) = thread { thread } else { - let new_usable_size = jem_malloc_usable_size_real( pointer ); - debug_assert!( new_usable_size >= effective_size ); - let new_tracking_pointer = tracking_pointer( pointer, new_usable_size ); - std::ptr::write_unaligned( new_tracking_pointer, InternalAllocationId::UNTRACKED ); + let new_usable_size = jem_malloc_usable_size_real(pointer); + debug_assert!(new_usable_size >= effective_size); + let new_tracking_pointer = tracking_pointer(pointer, new_usable_size); + std::ptr::write_unaligned(new_tracking_pointer, InternalAllocationId::UNTRACKED); return new_requested_size; }; - let backtrace = unwind::grab( &mut thread ); + let backtrace = unwind::grab(&mut thread); - let new_usable_size = jem_malloc_usable_size_real( pointer ); - debug_assert!( new_usable_size >= effective_size ); - let new_tracking_pointer = tracking_pointer( pointer, new_usable_size ); - std::ptr::write_unaligned( new_tracking_pointer, id ); + let new_usable_size = jem_malloc_usable_size_real(pointer); + debug_assert!(new_usable_size >= effective_size); + let new_tracking_pointer = tracking_pointer(pointer, new_usable_size); + std::ptr::write_unaligned(new_tracking_pointer, id); let allocation = InternalAllocation { address: address, size: new_requested_size as usize, - flags: translate_jemalloc_flags( flags ), + flags: translate_jemalloc_flags(flags), tid: thread.system_tid(), extra_usable_space: 0, }; - on_reallocation( id, address, allocation, backtrace, thread ); + on_reallocation(id, address, allocation, backtrace, thread); new_requested_size } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn _rjem_nallocx( requested_size: size_t, flags: c_int ) -> size_t { - let effective_size = match requested_size.checked_add( mem::size_of::< InternalAllocationId >() ) { - Some( size ) => size, - None => return 0 +pub unsafe extern "C" fn _rjem_nallocx(requested_size: size_t, flags: c_int) -> size_t { + let effective_size = match requested_size.checked_add(mem::size_of::()) { + Some(size) => size, + None => return 0, }; - jem_nallocx_real( effective_size, flags ).checked_sub( mem::size_of::< InternalAllocationId >() ).expect( "_rjem_nallocx: underflow" ) + jem_nallocx_real(effective_size, flags) + .checked_sub(mem::size_of::()) + .expect("_rjem_nallocx: underflow") } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn _rjem_malloc_usable_size( pointer: *mut c_void ) -> size_t { - let usable_size = jem_malloc_usable_size_real( pointer ); - match usable_size.checked_sub( mem::size_of::< InternalAllocationId >() ) { - Some( size ) => { - debug_assert!( std::ptr::read_unaligned( tracking_pointer( pointer, usable_size ) ).is_valid() ); +pub unsafe extern "C" fn _rjem_malloc_usable_size(pointer: *mut c_void) -> size_t { + let usable_size = jem_malloc_usable_size_real(pointer); + match usable_size.checked_sub(mem::size_of::()) { + Some(size) => { + debug_assert!( + std::ptr::read_unaligned(tracking_pointer(pointer, usable_size)).is_valid() + ); size - }, - None => panic!( "_rjem_malloc_usable_size: underflow (pointer=0x{:016X}, usable_size={})", pointer as usize , usable_size ) + } + None => panic!( + "_rjem_malloc_usable_size: underflow (pointer=0x{:016X}, usable_size={})", + pointer as usize, usable_size + ), } } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn _rjem_mallctl( name: *const libc::c_char, _oldp: *mut c_void, _oldlenp: *mut size_t, _newp: *mut c_void, _newlen: size_t ) -> c_int { - warn!( "unimplemented: rjem_mallctl called: name={:?}", std::ffi::CStr::from_ptr( name ) ); +pub unsafe extern "C" fn _rjem_mallctl( + name: *const libc::c_char, + _oldp: *mut c_void, + _oldlenp: *mut size_t, + _newp: *mut c_void, + _newlen: size_t, +) -> c_int { + warn!( + "unimplemented: rjem_mallctl called: name={:?}", + std::ffi::CStr::from_ptr(name) + ); 0 } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn _rjem_posix_memalign( memptr: *mut *mut c_void, alignment: size_t, requested_size: size_t ) -> c_int { +pub unsafe extern "C" fn _rjem_posix_memalign( + memptr: *mut *mut c_void, + alignment: size_t, + requested_size: size_t, +) -> c_int { if memptr.is_null() { return libc::EINVAL; } - let ptr_size = mem::size_of::< *const c_void >(); + let ptr_size = mem::size_of::<*const c_void>(); if alignment % ptr_size != 0 || !(alignment / ptr_size).is_power_of_two() || alignment == 0 { return libc::EINVAL; } - let pointer = jemalloc_allocate( requested_size, JeAllocationKind::Memalign( alignment ) ); + let pointer = jemalloc_allocate(requested_size, JeAllocationKind::Memalign(alignment)); *memptr = pointer; if pointer.is_null() { @@ -753,67 +883,75 @@ pub unsafe extern "C" fn _rjem_posix_memalign( memptr: *mut *mut c_void, alignme } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn _rjem_aligned_alloc( alignment: size_t, size: size_t ) -> *mut c_void { +pub unsafe extern "C" fn _rjem_aligned_alloc(alignment: size_t, size: size_t) -> *mut c_void { if !alignment.is_power_of_two() || size % alignment != 0 { *libc::__errno_location() = libc::EINVAL; return std::ptr::null_mut(); } - jemalloc_allocate( size, JeAllocationKind::AlignedAlloc( alignment ) ) + jemalloc_allocate(size, JeAllocationKind::AlignedAlloc(alignment)) } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn _rjem_memalign( _alignment: size_t, _size: size_t ) -> *mut c_void { - todo!( "_rjem_memalign" ); +pub unsafe extern "C" fn _rjem_memalign(_alignment: size_t, _size: size_t) -> *mut c_void { + todo!("_rjem_memalign"); } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn _rjem_valloc( _alignment: size_t, _size: size_t ) -> *mut c_void { - todo!( "_rjem_valloc" ); +pub unsafe extern "C" fn _rjem_valloc(_alignment: size_t, _size: size_t) -> *mut c_void { + todo!("_rjem_valloc"); } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn _rjem_free( pointer: *mut c_void ) { - let address = match NonZeroUsize::new( pointer as usize ) { - Some( address ) => address, - None => return +pub unsafe extern "C" fn _rjem_free(pointer: *mut c_void) { + let address = match NonZeroUsize::new(pointer as usize) { + Some(address) => address, + None => return, }; - let usable_size = jem_malloc_usable_size_real( pointer ); - let tracking_pointer = tracking_pointer( pointer, usable_size ); - let id = std::ptr::read_unaligned( tracking_pointer ); - debug_assert!( id.is_valid() ); + let usable_size = jem_malloc_usable_size_real(pointer); + let tracking_pointer = tracking_pointer(pointer, usable_size); + let id = std::ptr::read_unaligned(tracking_pointer); + debug_assert!(id.is_valid()); let mut thread = StrongThreadHandle::acquire(); - jem_free_real( pointer ); + jem_free_real(pointer); if id.is_untracked() && !crate::global::is_actively_running() { thread = None; } - let mut thread = if let Some( thread ) = thread { thread } else { return }; + let mut thread = if let Some(thread) = thread { + thread + } else { + return; + }; let backtrace = if opt::get().grab_backtraces_on_free { - Some( unwind::grab( &mut thread ) ) + Some(unwind::grab(&mut thread)) } else { None }; - on_free( id, address, backtrace, thread ); + on_free(id, address, backtrace, thread); } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn _rjem_sallocx( _pointer: *const c_void, _flags: c_int ) -> size_t { - todo!( "_rjem_sallocx" ); +pub unsafe extern "C" fn _rjem_sallocx(_pointer: *const c_void, _flags: c_int) -> size_t { + todo!("_rjem_sallocx"); } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn _rjem_dallocx( _pointer: *mut c_void, _flags: c_int ) { - todo!( "_rjem_dallocx" ); +pub unsafe extern "C" fn _rjem_dallocx(_pointer: *mut c_void, _flags: c_int) { + todo!("_rjem_dallocx"); } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn _rjem_mallctlnametomib( name: *const libc::c_char, mibp: *mut size_t, miblenp: *mut size_t ) -> c_int { - jem_mallctlnametomib_real( name, mibp, miblenp ) +pub unsafe extern "C" fn _rjem_mallctlnametomib( + name: *const libc::c_char, + mibp: *mut size_t, + miblenp: *mut size_t, +) -> c_int { + jem_mallctlnametomib_real(name, mibp, miblenp) } #[cfg_attr(not(test), no_mangle)] @@ -825,30 +963,34 @@ pub unsafe extern "C" fn _rjem_mallctlbymib( newp: *mut c_void, newlen: size_t, ) -> c_int { - jem_mallctlbymib_real( mib, miblen, oldp, oldpenp, newp, newlen ) + jem_mallctlbymib_real(mib, miblen, oldp, oldpenp, newp, newlen) } #[cfg_attr(not(test), no_mangle)] pub unsafe extern "C" fn _rjem_malloc_stats_print( - write_cb: Option< unsafe extern "C" fn( *mut c_void, *const libc::c_char ) >, + write_cb: Option, cbopaque: *mut c_void, opts: *const libc::c_char, ) { - jem_malloc_stats_print_real( write_cb, cbopaque, opts ) + jem_malloc_stats_print_real(write_cb, cbopaque, opts) } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn posix_memalign( memptr: *mut *mut c_void, alignment: size_t, requested_size: size_t ) -> c_int { +pub unsafe extern "C" fn posix_memalign( + memptr: *mut *mut c_void, + alignment: size_t, + requested_size: size_t, +) -> c_int { if memptr.is_null() { return libc::EINVAL; } - let ptr_size = mem::size_of::< *const c_void >(); + let ptr_size = mem::size_of::<*const c_void>(); if alignment % ptr_size != 0 || !(alignment / ptr_size).is_power_of_two() || alignment == 0 { return libc::EINVAL; } - let pointer = allocate( requested_size, AllocationKind::Aligned( alignment ) ); + let pointer = allocate(requested_size, AllocationKind::Aligned(alignment)); *memptr = pointer; if pointer.is_null() { @@ -859,53 +1001,125 @@ pub unsafe extern "C" fn posix_memalign( memptr: *mut *mut c_void, alignment: si } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn mmap( addr: *mut c_void, length: size_t, prot: c_int, flags: c_int, fildes: c_int, off: off_t ) -> *mut c_void { - mmap_internal( addr, length, prot, flags, fildes, off as libc::off64_t, MapKind::Mmap ) +pub unsafe extern "C" fn mmap( + addr: *mut c_void, + length: size_t, + prot: c_int, + flags: c_int, + fildes: c_int, + off: off_t, +) -> *mut c_void { + mmap_internal( + addr, + length, + prot, + flags, + fildes, + off as libc::off64_t, + MapKind::Mmap, + ) } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn mmap64( addr: *mut c_void, length: size_t, prot: c_int, flags: c_int, fildes: c_int, off: libc::off64_t ) -> *mut c_void { - mmap_internal( addr, length, prot, flags, fildes, off, MapKind::Mmap ) -} - -pub unsafe extern "C" fn __mmap( addr: *mut c_void, length: size_t, prot: c_int, flags: c_int, fildes: c_int, off: off_t ) -> *mut c_void { - mmap_internal( addr, length, prot, flags, fildes, off as libc::off64_t, MapKind::Glibc ) +pub unsafe extern "C" fn mmap64( + addr: *mut c_void, + length: size_t, + prot: c_int, + flags: c_int, + fildes: c_int, + off: libc::off64_t, +) -> *mut c_void { + mmap_internal(addr, length, prot, flags, fildes, off, MapKind::Mmap) +} + +pub unsafe extern "C" fn __mmap( + addr: *mut c_void, + length: size_t, + prot: c_int, + flags: c_int, + fildes: c_int, + off: off_t, +) -> *mut c_void { + mmap_internal( + addr, + length, + prot, + flags, + fildes, + off as libc::off64_t, + MapKind::Glibc, + ) } #[inline(always)] -unsafe fn call_mmap( addr: *mut c_void, length: size_t, prot: c_int, flags: c_int, fildes: c_int, off: libc::off64_t ) -> *mut c_void { +unsafe fn call_mmap( + addr: *mut c_void, + length: size_t, + prot: c_int, + flags: c_int, + fildes: c_int, + off: libc::off64_t, +) -> *mut c_void { let mut ptr = libc::MAP_FAILED; - if !crate::global::is_pr_set_vma_anon_name_supported() && flags & (libc::MAP_FIXED | libc::MAP_FIXED_NOREPLACE) == 0 { - let p0 = syscall::mmap( std::ptr::null_mut(), length + 8192, 0, libc::MAP_PRIVATE, crate::global::dummy_memfd(), 0 ); + if !crate::global::is_pr_set_vma_anon_name_supported() + && flags & (libc::MAP_FIXED | libc::MAP_FIXED_NOREPLACE) == 0 + { + let p0 = syscall::mmap( + std::ptr::null_mut(), + length + 8192, + 0, + libc::MAP_PRIVATE, + crate::global::dummy_memfd(), + 0, + ); if p0 != libc::MAP_FAILED { - ptr = syscall::mmap( p0.add( 4096 ), length, prot, flags | libc::MAP_FIXED, fildes, off ); + ptr = syscall::mmap( + p0.add(4096), + length, + prot, + flags | libc::MAP_FIXED, + fildes, + off, + ); if ptr == libc::MAP_FAILED { - error!( "Failed to mmap over a dummy mapping!" ); + error!("Failed to mmap over a dummy mapping!"); libc::abort(); } } } else { - ptr = syscall::mmap( addr, length, prot, flags, fildes, off ) + ptr = syscall::mmap(addr, length, prot, flags, fildes, off) } ptr } #[inline(always)] -unsafe fn mmap_internal( addr: *mut c_void, length: size_t, prot: c_int, flags: c_int, fildes: c_int, off: libc::off64_t, kind: MapKind ) -> *mut c_void { +unsafe fn mmap_internal( + addr: *mut c_void, + length: size_t, + prot: c_int, + flags: c_int, + fildes: c_int, + off: libc::off64_t, + kind: MapKind, +) -> *mut c_void { if !opt::is_initialized() || !opt::get().gather_maps { - return syscall::mmap( addr, length, prot, flags, fildes, off ); + return syscall::mmap(addr, length, prot, flags, fildes, off); } let id = crate::global::next_map_id(); - if let Some( mut thread ) = crate::global::acquire_any_thread_handle() { - if let Some( backtrace ) = unwind::grab_from_any( &mut thread ) { + if let Some(mut thread) = crate::global::acquire_any_thread_handle() { + if let Some(backtrace) = unwind::grab_from_any(&mut thread) { let mut maps_registry = crate::global::MMAP_REGISTRY.lock().unwrap(); let timestamp = get_timestamp(); - let ptr = call_mmap( addr, length, prot, flags, fildes, off ); + let ptr = call_mmap(addr, length, prot, flags, fildes, off); if ptr != libc::MAP_FAILED { let range = ptr as u64..ptr as u64 + length as u64; - let source = MapSource { timestamp, backtrace: backtrace.clone(), tid: thread.system_tid() }; + let source = MapSource { + timestamp, + backtrace: backtrace.clone(), + tid: thread.system_tid(), + }; maps_registry.on_mmap( id, range, @@ -914,10 +1128,10 @@ unsafe fn mmap_internal( addr: *mut c_void, length: size_t, prot: c_int, flags: prot as u32, flags as u32, fildes as u32, - off as u64 + off as u64, ); - maps_registry.set_vma_name( ptr, length, id, kind ); + maps_registry.set_vma_name(ptr, length, id, kind); } return ptr; @@ -925,34 +1139,45 @@ unsafe fn mmap_internal( addr: *mut c_void, length: size_t, prot: c_int, flags: } let mut maps_registry = crate::global::MMAP_REGISTRY.lock().unwrap(); - let ptr = call_mmap( addr, length, prot, flags, fildes, off ); - maps_registry.set_vma_name( ptr, length, id, kind ); + let ptr = call_mmap(addr, length, prot, flags, fildes, off); + maps_registry.set_vma_name(ptr, length, id, kind); ptr } #[inline(always)] -unsafe extern "C" fn munmap_internal( ptr: *mut c_void, length: size_t ) -> c_int { +unsafe extern "C" fn munmap_internal(ptr: *mut c_void, length: size_t) -> c_int { if !opt::is_initialized() || !opt::get().gather_maps { - return syscall::munmap( ptr, length ); + return syscall::munmap(ptr, length); } - if let Some( mut thread ) = crate::global::acquire_any_thread_handle() { - if let Some( backtrace ) = unwind::grab_from_any( &mut thread ) { + if let Some(mut thread) = crate::global::acquire_any_thread_handle() { + if let Some(backtrace) = unwind::grab_from_any(&mut thread) { let mut maps_registry = crate::global::MMAP_REGISTRY.lock().unwrap(); let timestamp = get_timestamp(); let result; if !crate::global::is_pr_set_vma_anon_name_supported() { - result = syscall::mmap( ptr, length, 0, libc::MAP_PRIVATE | libc::MAP_FIXED, crate::global::dummy_memfd(), 0 ) != libc::MAP_FAILED; + result = syscall::mmap( + ptr, + length, + 0, + libc::MAP_PRIVATE | libc::MAP_FIXED, + crate::global::dummy_memfd(), + 0, + ) != libc::MAP_FAILED; } else { - result = syscall::munmap( ptr, length ) == 0; + result = syscall::munmap(ptr, length) == 0; } if result { let range = ptr as u64..ptr as u64 + length as u64; - let source = MapSource { timestamp, backtrace: backtrace.clone(), tid: thread.system_tid() }; - maps_registry.on_munmap( range, source ); - maps_registry.clear_vma_name( ptr, length ); + let source = MapSource { + timestamp, + backtrace: backtrace.clone(), + tid: thread.system_tid(), + }; + maps_registry.on_munmap(range, source); + maps_registry.clear_vma_name(ptr, length); } if result { @@ -964,41 +1189,45 @@ unsafe extern "C" fn munmap_internal( ptr: *mut c_void, length: size_t ) -> c_in } let mut maps_registry = crate::global::MMAP_REGISTRY.lock().unwrap(); - let result = syscall::munmap( ptr, length ); - maps_registry.clear_vma_name( ptr, length ); + let result = syscall::munmap(ptr, length); + maps_registry.clear_vma_name(ptr, length); result } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn munmap( ptr: *mut c_void, length: size_t ) -> c_int { - munmap_internal( ptr, length ) +pub unsafe extern "C" fn munmap(ptr: *mut c_void, length: size_t) -> c_int { + munmap_internal(ptr, length) } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn __munmap( ptr: *mut c_void, length: size_t ) -> c_int { - munmap_internal( ptr, length ) +pub unsafe extern "C" fn __munmap(ptr: *mut c_void, length: size_t) -> c_int { + munmap_internal(ptr, length) } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn mallopt( param: c_int, value: c_int ) -> c_int { +pub unsafe extern "C" fn mallopt(param: c_int, value: c_int) -> c_int { if crate::global::using_unprefixed_jemalloc() { return 0; } let thread = StrongThreadHandle::acquire(); - let result = libc_mallopt_real( param, value ); + let result = libc_mallopt_real(param, value); - let mut thread = if let Some( thread ) = thread { thread } else { return result }; - let backtrace = unwind::grab( &mut thread ); + let mut thread = if let Some(thread) = thread { + thread + } else { + return result; + }; + let backtrace = unwind::grab(&mut thread); let timestamp = get_timestamp(); - send_event_throttled( || InternalEvent::Mallopt { + send_event_throttled(|| InternalEvent::Mallopt { param: param as i32, value: value as i32, result: result as i32, backtrace, timestamp, - thread: thread.decay() + thread: thread.decay(), }); result @@ -1010,118 +1239,116 @@ pub unsafe extern "C" fn fork() -> libc::pid_t { if pid == 0 { crate::global::on_fork(); } else { - info!( "Fork called; child PID: {}", pid ); + info!("Fork called; child PID: {}", pid); } pid } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn memalign( alignment: size_t, size: size_t ) -> *mut c_void { +pub unsafe extern "C" fn memalign(alignment: size_t, size: size_t) -> *mut c_void { if !alignment.is_power_of_two() { *libc::__errno_location() = libc::EINVAL; return std::ptr::null_mut(); } - allocate( size, AllocationKind::Aligned( alignment ) ) + allocate(size, AllocationKind::Aligned(alignment)) } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn aligned_alloc( alignment: size_t, size: size_t ) -> *mut c_void { +pub unsafe extern "C" fn aligned_alloc(alignment: size_t, size: size_t) -> *mut c_void { if !alignment.is_power_of_two() || size % alignment != 0 { *libc::__errno_location() = libc::EINVAL; return std::ptr::null_mut(); } - allocate( size, AllocationKind::Aligned( alignment ) ) + allocate(size, AllocationKind::Aligned(alignment)) } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn valloc( _size: size_t ) -> *mut c_void { - unimplemented!( "'valloc' is unimplemented!" ); +pub unsafe extern "C" fn valloc(_size: size_t) -> *mut c_void { + unimplemented!("'valloc' is unimplemented!"); } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn pvalloc( _size: size_t ) -> *mut c_void { - unimplemented!( "'pvalloc' is unimplemented!" ); +pub unsafe extern "C" fn pvalloc(_size: size_t) -> *mut c_void { + unimplemented!("'pvalloc' is unimplemented!"); } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn memory_profiler_set_marker( value: u32 ) { +pub unsafe extern "C" fn memory_profiler_set_marker(value: u32) { let thread = StrongThreadHandle::acquire(); - send_event( InternalEvent::SetMarker { - value - }); + send_event(InternalEvent::SetMarker { value }); - mem::drop( thread ); + mem::drop(thread); } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn memory_profiler_override_next_timestamp( timestamp: u64 ) { +pub unsafe extern "C" fn memory_profiler_override_next_timestamp(timestamp: u64) { let thread = StrongThreadHandle::acquire(); - send_event_throttled( || InternalEvent::OverrideNextTimestamp { - timestamp: Timestamp::from_usecs( timestamp ) + send_event_throttled(|| InternalEvent::OverrideNextTimestamp { + timestamp: Timestamp::from_usecs(timestamp), }); - mem::drop( thread ); + mem::drop(thread); } fn sync() { let thread = StrongThreadHandle::acquire(); crate::event::flush(); crate::global::sync(); - mem::drop( thread ); + mem::drop(thread); } #[cfg_attr(not(test), no_mangle)] pub unsafe extern "C" fn memory_profiler_start() { - debug!( "Start called..." ); + debug!("Start called..."); if crate::global::enable() { sync(); } - debug!( "Start finished" ); + debug!("Start finished"); } #[cfg_attr(not(test), no_mangle)] pub unsafe extern "C" fn memory_profiler_stop() { - debug!( "Stop called..." ); + debug!("Stop called..."); if crate::global::disable() { sync(); } - debug!( "Stop finished" ); + debug!("Stop finished"); } #[cfg_attr(not(test), no_mangle)] pub unsafe extern "C" fn memory_profiler_sync() { - debug!( "Sync called..." ); + debug!("Sync called..."); sync(); - debug!( "Sync finished" ); + debug!("Sync finished"); } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn __register_frame( fde: *const u8 ) { - debug!( "Registering new frame: 0x{:016X}", fde as usize ); +pub unsafe extern "C" fn __register_frame(fde: *const u8) { + debug!("Registering new frame: 0x{:016X}", fde as usize); - if let Some( original ) = crate::global::SYM_REGISTER_FRAME { - original( fde ) + if let Some(original) = crate::global::SYM_REGISTER_FRAME { + original(fde) } else { - error!( "__register_frame call ignored since we couldn't find the original symbol" ); + error!("__register_frame call ignored since we couldn't find the original symbol"); } let thread = StrongThreadHandle::acquire(); - unwind::register_frame_by_pointer( fde ); - std::mem::drop( thread ); + unwind::register_frame_by_pointer(fde); + std::mem::drop(thread); } #[cfg_attr(not(test), no_mangle)] -pub unsafe extern "C" fn __deregister_frame( fde: *const u8 ) { - debug!( "Deregistering new frame: 0x{:016X}", fde as usize ); +pub unsafe extern "C" fn __deregister_frame(fde: *const u8) { + debug!("Deregistering new frame: 0x{:016X}", fde as usize); - if let Some( original ) = crate::global::SYM_DEREGISTER_FRAME { - original( fde ) + if let Some(original) = crate::global::SYM_DEREGISTER_FRAME { + original(fde) } else { - error!( "__deregister_frame call ignored since we couldn't find the original symbol" ); + error!("__deregister_frame call ignored since we couldn't find the original symbol"); } let thread = StrongThreadHandle::acquire(); - unwind::deregister_frame_by_pointer( fde ); - std::mem::drop( thread ); + unwind::deregister_frame_by_pointer(fde); + std::mem::drop(thread); } diff --git a/preload/src/arc_lite.rs b/preload/src/arc_lite.rs index 29ede150..0c8f91eb 100644 --- a/preload/src/arc_lite.rs +++ b/preload/src/arc_lite.rs @@ -1,86 +1,82 @@ -use std::ptr::NonNull; -use std::sync::atomic::{self, AtomicUsize, Ordering}; use std::mem; use std::ops::Deref; +use std::ptr::NonNull; +use std::sync::atomic::{self, AtomicUsize, Ordering}; -struct Inner< T > { +struct Inner { counter: AtomicUsize, - value: T + value: T, } -pub struct ArcLite< T >( NonNull< Inner< T > > ); -unsafe impl< T > Send for ArcLite< T > where T: Sync + Send {} -unsafe impl< T > Sync for ArcLite< T > where T: Sync + Send {} +pub struct ArcLite(NonNull>); +unsafe impl Send for ArcLite where T: Sync + Send {} +unsafe impl Sync for ArcLite where T: Sync + Send {} -impl< T > ArcLite< T > { - pub fn new( value: T ) -> Self { - let ptr = Box::into_raw( Box::new( Inner { - counter: AtomicUsize::new( 1 ), - value +impl ArcLite { + pub fn new(value: T) -> Self { + let ptr = Box::into_raw(Box::new(Inner { + counter: AtomicUsize::new(1), + value, })); - unsafe { - ArcLite( NonNull::new_unchecked( ptr ) ) - } + unsafe { ArcLite(NonNull::new_unchecked(ptr)) } } - pub fn ptr_eq( lhs: &Self, rhs: &Self ) -> bool { + pub fn ptr_eq(lhs: &Self, rhs: &Self) -> bool { lhs.0 == rhs.0 } #[inline] - unsafe fn counter( arc: &Self ) -> &AtomicUsize { + unsafe fn counter(arc: &Self) -> &AtomicUsize { &arc.0.as_ref().counter } #[inline] - pub fn get_refcount_relaxed( arc: &Self ) -> usize { - unsafe { - Self::counter( arc ).load( Ordering::Relaxed ) - } + pub fn get_refcount_relaxed(arc: &Self) -> usize { + unsafe { Self::counter(arc).load(Ordering::Relaxed) } } #[inline(never)] - unsafe fn drop_slow( arc: &mut Self ) { - mem::drop( Box::from_raw( arc.0.as_ptr() ) ); + unsafe fn drop_slow(arc: &mut Self) { + mem::drop(Box::from_raw(arc.0.as_ptr())); } - pub unsafe fn add( arc: &Self, value: usize ) { - Self::counter( arc ).fetch_add( value, Ordering::SeqCst ); + pub unsafe fn add(arc: &Self, value: usize) { + Self::counter(arc).fetch_add(value, Ordering::SeqCst); } - pub unsafe fn sub( arc: &Self, value: usize ) { - Self::counter( arc ).fetch_sub( value, Ordering::SeqCst ); + pub unsafe fn sub(arc: &Self, value: usize) { + Self::counter(arc).fetch_sub(value, Ordering::SeqCst); } } -impl< T > Deref for ArcLite< T > { +impl Deref for ArcLite { type Target = T; - fn deref( &self ) -> &Self::Target { + fn deref(&self) -> &Self::Target { &unsafe { self.0.as_ref() }.value } } -impl< T > Clone for ArcLite< T > { +impl Clone for ArcLite { #[inline] - fn clone( &self ) -> Self { + fn clone(&self) -> Self { unsafe { - ArcLite::counter( self ).fetch_add( 1, Ordering::Relaxed ); + ArcLite::counter(self).fetch_add(1, Ordering::Relaxed); } - ArcLite( self.0 ) + ArcLite(self.0) } } -impl< T > Drop for ArcLite< T > { +impl Drop for ArcLite { #[inline] - fn drop( &mut self ) { + fn drop(&mut self) { unsafe { - if ArcLite::counter( self ).fetch_sub( 1, Ordering::Release ) != 1 { + if ArcLite::counter(self).fetch_sub(1, Ordering::Release) != 1 { return; } - atomic::fence( Ordering::Acquire ); - ArcLite::drop_slow( self ); + atomic::fence(Ordering::Acquire); + ArcLite::drop_slow(self); } } } diff --git a/preload/src/channel.rs b/preload/src/channel.rs index 680c37d6..f896cc55 100644 --- a/preload/src/channel.rs +++ b/preload/src/channel.rs @@ -1,67 +1,67 @@ -use std::time::Duration; use std::mem; +use std::time::Duration; -use std::sync::{Mutex, Condvar}; use crate::utils::CacheAligned; +use std::sync::{Condvar, Mutex}; const CHUNK_SIZE: usize = 64; #[repr(C)] -pub struct Channel< T > { - queues: [CacheAligned< Mutex< Vec< T > > >; 5], - condvar: CacheAligned< Condvar > +pub struct Channel { + queues: [CacheAligned>>; 5], + condvar: CacheAligned, } -pub struct ChannelBuffer< T > { - queues: [Vec< T >; 5] +pub struct ChannelBuffer { + queues: [Vec; 5], } -impl< T > Default for ChannelBuffer< T > { +impl Default for ChannelBuffer { fn default() -> Self { Self::new() } } -impl< T > ChannelBuffer< T > { +impl ChannelBuffer { pub fn new() -> Self { ChannelBuffer { - queues: Default::default() + queues: Default::default(), } } - pub fn is_empty( &self ) -> bool { - self.queues.iter().all( |queue| queue.is_empty() ) + pub fn is_empty(&self) -> bool { + self.queues.iter().all(|queue| queue.is_empty()) } - pub fn extend( &mut self, iter: impl IntoIterator< Item = T > ) { - self.queues[0].extend( iter ); + pub fn extend(&mut self, iter: impl IntoIterator) { + self.queues[0].extend(iter); } - pub fn drain< 'a >( &'a mut self ) -> impl Iterator< Item = T > + 'a { - self.queues.iter_mut().flat_map( |queue| queue.drain(..) ) + pub fn drain<'a>(&'a mut self) -> impl Iterator + 'a { + self.queues.iter_mut().flat_map(|queue| queue.drain(..)) } } -impl< T > Channel< T > { +impl Channel { pub const fn new() -> Self { Channel { queues: [ - CacheAligned( Mutex::new( Vec::new() ) ), - CacheAligned( Mutex::new( Vec::new() ) ), - CacheAligned( Mutex::new( Vec::new() ) ), - CacheAligned( Mutex::new( Vec::new() ) ), - CacheAligned( Mutex::new( Vec::new() ) ), + CacheAligned(Mutex::new(Vec::new())), + CacheAligned(Mutex::new(Vec::new())), + CacheAligned(Mutex::new(Vec::new())), + CacheAligned(Mutex::new(Vec::new())), + CacheAligned(Mutex::new(Vec::new())), ], - condvar: CacheAligned( Condvar::new() ) + condvar: CacheAligned(Condvar::new()), } } - pub fn timed_recv_all( &self, buffer: &mut ChannelBuffer< T >, duration: Duration ) { + pub fn timed_recv_all(&self, buffer: &mut ChannelBuffer, duration: Duration) { let mut found = false; - for (queue, output) in self.queues.iter().zip( buffer.queues.iter_mut() ) { + for (queue, output) in self.queues.iter().zip(buffer.queues.iter_mut()) { let mut guard = queue.lock().unwrap(); if !guard.is_empty() { - std::mem::swap( &mut *guard, output ); + std::mem::swap(&mut *guard, output); found = true; } } @@ -69,50 +69,50 @@ impl< T > Channel< T > { if !found { let mut guard = self.queues[0].lock().unwrap(); if guard.is_empty() { - guard = self.condvar.wait_timeout( guard, duration ).unwrap().0; + guard = self.condvar.wait_timeout(guard, duration).unwrap().0; } - mem::swap( &mut *guard, &mut buffer.queues[0] ); + mem::swap(&mut *guard, &mut buffer.queues[0]); } } - pub fn send( &self, value: T ) -> usize { - self.send_with( || value ) + pub fn send(&self, value: T) -> usize { + self.send_with(|| value) } - pub fn send_with< F: FnOnce() -> T >( &self, callback: F ) -> usize { + pub fn send_with T>(&self, callback: F) -> usize { let mut guard = self.queues[0].lock().unwrap(); self.condvar.notify_all(); - guard.reserve( 1 ); - guard.push( callback() ); + guard.reserve(1); + guard.push(callback()); guard.len() } - pub fn chunked_send_with< F: FnOnce() -> T >( &self, callback: F ) -> usize { + pub fn chunked_send_with T>(&self, callback: F) -> usize { let mut guard = self.queues[0].lock().unwrap(); let length = guard.len() + 1; if length % CHUNK_SIZE == 0 { self.condvar.notify_all(); } - guard.reserve( 1 ); - guard.push( callback() ); + guard.reserve(1); + guard.push(callback()); length } - pub fn sharded_chunked_send_with< F: FnOnce() -> T >( &self, key: usize, callback: F ) -> usize { + pub fn sharded_chunked_send_with T>(&self, key: usize, callback: F) -> usize { let queue_index = key & 0b11 + 1; - let mut guard = self.queues[ queue_index ].lock().unwrap(); + let mut guard = self.queues[queue_index].lock().unwrap(); let length = guard.len() + 1; if length % CHUNK_SIZE == 0 { self.condvar.notify_all(); } - guard.reserve( 1 ); - guard.push( callback() ); + guard.reserve(1); + guard.push(callback()); length } - pub fn flush( &self ) { + pub fn flush(&self) { self.condvar.notify_all(); } } diff --git a/preload/src/elf.rs b/preload/src/elf.rs index d2571595..f77d2172 100644 --- a/preload/src/elf.rs +++ b/preload/src/elf.rs @@ -25,8 +25,8 @@ */ use libc::{c_char, c_void, size_t}; -use std::ops::ControlFlow; use std::ffi::CStr; +use std::ops::ControlFlow; const STT_GNU_IFUNC: u8 = 10; @@ -48,109 +48,121 @@ pub struct Dynamic { value: usize, } -pub struct ObjectInfo< 'a > { +pub struct ObjectInfo<'a> { pub address: usize, pub name: &'a [u8], - program_headers: &'a [ProgramHeader] + program_headers: &'a [ProgramHeader], } -impl< 'a > ObjectInfo< 'a > { - pub fn each< F, R >( mut callback: F ) -> Option< R > where F: FnMut( ObjectInfo ) -> ControlFlow< R, () > { - Self::each_impl( &mut callback ) +impl<'a> ObjectInfo<'a> { + pub fn each(mut callback: F) -> Option + where + F: FnMut(ObjectInfo) -> ControlFlow, + { + Self::each_impl(&mut callback) } - pub fn name_contains( &self, substr: impl AsRef< [u8] > ) -> bool { + pub fn name_contains(&self, substr: impl AsRef<[u8]>) -> bool { let substr = substr.as_ref(); - self.name.windows( substr.len() ).any( |window| window == substr ) + self.name + .windows(substr.len()) + .any(|window| window == substr) } - fn each_impl< R >( callback: &mut dyn FnMut( ObjectInfo ) -> ControlFlow< R, () > ) -> Option< R > { - struct Arg< 'a, R > { - callback: &'a mut dyn FnMut( ObjectInfo ) -> ControlFlow< R, () >, - result: Option< R > + fn each_impl(callback: &mut dyn FnMut(ObjectInfo) -> ControlFlow) -> Option { + struct Arg<'a, R> { + callback: &'a mut dyn FnMut(ObjectInfo) -> ControlFlow, + result: Option, } let mut arg = Arg { callback, - result: None + result: None, }; - extern "C" fn callback_static< R >( info: *mut libc::dl_phdr_info, _size: size_t, arg: *mut c_void ) -> i32 { + extern "C" fn callback_static( + info: *mut libc::dl_phdr_info, + _size: size_t, + arg: *mut c_void, + ) -> i32 { let info = unsafe { &*info }; - let arg = unsafe { &mut *(arg as *mut Arg< R >) }; + let arg = unsafe { &mut *(arg as *mut Arg) }; let info = ObjectInfo { address: info.dlpi_addr as usize, - name: - if info.dlpi_name.is_null() { - &[] - } else { - unsafe { - CStr::from_ptr( info.dlpi_name ).to_bytes() - } - }, - program_headers: - if info.dlpi_phnum == 0 { - &[] - } else { - unsafe { - std::slice::from_raw_parts( info.dlpi_phdr, info.dlpi_phnum as usize ) - } - } + name: if info.dlpi_name.is_null() { + &[] + } else { + unsafe { CStr::from_ptr(info.dlpi_name).to_bytes() } + }, + program_headers: if info.dlpi_phnum == 0 { + &[] + } else { + unsafe { std::slice::from_raw_parts(info.dlpi_phdr, info.dlpi_phnum as usize) } + }, }; - let result = (arg.callback)( info ); + let result = (arg.callback)(info); match result { - ControlFlow::Break( result ) => { - arg.result = Some( result ); + ControlFlow::Break(result) => { + arg.result = Some(result); 1 - }, - ControlFlow::Continue(()) => { - 0 } + ControlFlow::Continue(()) => 0, } } unsafe { - libc::dl_iterate_phdr( Some( callback_static::< R > ), (&mut arg) as *mut Arg< R > as *mut c_void ); + libc::dl_iterate_phdr( + Some(callback_static::), + (&mut arg) as *mut Arg as *mut c_void, + ); } arg.result } - pub fn dlsym( &self, name: impl AsRef< [u8] > ) -> Option< *mut c_void > { - self.dlsym_impl( name.as_ref() ) + pub fn dlsym(&self, name: impl AsRef<[u8]>) -> Option<*mut c_void> { + self.dlsym_impl(name.as_ref()) } - fn dlsym_impl( &self, name: &[u8] ) -> Option< *mut c_void > { + fn dlsym_impl(&self, name: &[u8]) -> Option<*mut c_void> { let mut dt_hash = None; let mut dt_strtab = None; let mut dt_symtab = None; let mut dt_gnu_hash = None; for dynamic in self.dynamic() { match dynamic.tag { - 4 if self.check_address( dynamic.value ) => dt_hash = Some( dynamic.value as *const u32 ), - 5 if self.check_address( dynamic.value ) => dt_strtab = Some( dynamic.value as *const u8 ), - 6 if self.check_address( dynamic.value ) => dt_symtab = Some( dynamic.value as *const Symbol ), - 0x6fff_fef5 if self.check_address( dynamic.value ) => dt_gnu_hash = Some( dynamic.value as *const u32 ), + 4 if self.check_address(dynamic.value) => { + dt_hash = Some(dynamic.value as *const u32) + } + 5 if self.check_address(dynamic.value) => { + dt_strtab = Some(dynamic.value as *const u8) + } + 6 if self.check_address(dynamic.value) => { + dt_symtab = Some(dynamic.value as *const Symbol) + } + 0x6fff_fef5 if self.check_address(dynamic.value) => { + dt_gnu_hash = Some(dynamic.value as *const u32) + } _ => {} } } - if let (Some( dt_strtab ), Some( dt_symtab )) = (dt_strtab, dt_symtab) { - let result_gnu_hash = dt_gnu_hash.and_then( |dt_gnu_hash| unsafe { - self.dlsym_gnu_hash( dt_strtab, dt_symtab, dt_gnu_hash, name ) + if let (Some(dt_strtab), Some(dt_symtab)) = (dt_strtab, dt_symtab) { + let result_gnu_hash = dt_gnu_hash.and_then(|dt_gnu_hash| unsafe { + self.dlsym_gnu_hash(dt_strtab, dt_symtab, dt_gnu_hash, name) }); - if result_gnu_hash.is_some() && !cfg!( test ) { + if result_gnu_hash.is_some() && !cfg!(test) { return result_gnu_hash; } - let result_elf_hash = dt_hash.and_then( |dt_hash| unsafe { - self.dlsym_elf_hash( dt_strtab, dt_symtab, dt_hash, name ) + let result_elf_hash = dt_hash.and_then(|dt_hash| unsafe { + self.dlsym_elf_hash(dt_strtab, dt_symtab, dt_hash, name) }); - if cfg!( test ) { - assert_eq!( result_gnu_hash, result_elf_hash ); + if cfg!(test) { + assert_eq!(result_gnu_hash, result_elf_hash); } return result_elf_hash; @@ -159,11 +171,17 @@ impl< 'a > ObjectInfo< 'a > { None } - unsafe fn dlsym_gnu_hash( &self, dt_strtab: *const u8, dt_symtab: *const Symbol, dt_gnu_hash: *const u32, name: &[u8] ) -> Option< *mut c_void > { - fn calculate_hash( name: &[u8] ) -> u32 { + unsafe fn dlsym_gnu_hash( + &self, + dt_strtab: *const u8, + dt_symtab: *const Symbol, + dt_gnu_hash: *const u32, + name: &[u8], + ) -> Option<*mut c_void> { + fn calculate_hash(name: &[u8]) -> u32 { let mut hash: u32 = 5381; for &byte in name { - hash = ( hash << 5 ).wrapping_add( hash ).wrapping_add( byte as u32 ) + hash = (hash << 5).wrapping_add(hash).wrapping_add(byte as u32) } hash @@ -174,18 +192,21 @@ impl< 'a > ObjectInfo< 'a > { } let nbuckets: u32 = *dt_gnu_hash; - let symbias: u32 = *dt_gnu_hash.add( 1 ); - let bitmask_nwords: u32 = *dt_gnu_hash.add( 2 ); + let symbias: u32 = *dt_gnu_hash.add(1); + let bitmask_nwords: u32 = *dt_gnu_hash.add(2); let bitmask_idxbits: u32 = bitmask_nwords - 1; - let shift: u32 = *dt_gnu_hash.add( 3 ); - let bitmask: *const usize = dt_gnu_hash.add( 4 ).cast(); - let buckets: *const u32 = dt_gnu_hash.add( 4 + (std::mem::size_of::< usize >() / 4) * bitmask_nwords as usize ); - let chain_zero: *const u32 = buckets.wrapping_add( (nbuckets as usize).wrapping_sub( symbias as usize ) ); - - let hash: u32 = calculate_hash( name ); - - let bitmask_word: usize = *bitmask.add((hash as usize / (std::mem::size_of::< usize >() * 8)) & (bitmask_idxbits as usize)); - let mask = (std::mem::size_of::< usize >() as u32) * 8 - 1; + let shift: u32 = *dt_gnu_hash.add(3); + let bitmask: *const usize = dt_gnu_hash.add(4).cast(); + let buckets: *const u32 = + dt_gnu_hash.add(4 + (std::mem::size_of::() / 4) * bitmask_nwords as usize); + let chain_zero: *const u32 = + buckets.wrapping_add((nbuckets as usize).wrapping_sub(symbias as usize)); + + let hash: u32 = calculate_hash(name); + + let bitmask_word: usize = *bitmask + .add((hash as usize / (std::mem::size_of::() * 8)) & (bitmask_idxbits as usize)); + let mask = (std::mem::size_of::() as u32) * 8 - 1; let hashbit1: u32 = hash & mask; let hashbit2: u32 = (hash >> shift) & mask; @@ -193,17 +214,20 @@ impl< 'a > ObjectInfo< 'a > { return None; } - let bucket = *buckets.add( (hash % nbuckets) as usize ); + let bucket = *buckets.add((hash % nbuckets) as usize); if bucket == 0 { return None; } - let mut hasharr: *const u32 = chain_zero.add( bucket as usize ); + let mut hasharr: *const u32 = chain_zero.add(bucket as usize); loop { if ((*hasharr ^ hash) >> 1) == 0 { - let symtab_offset = ((hasharr as usize) - (chain_zero as usize)) / std::mem::size_of::< u32 >(); - if let Some( pointer ) = self.resolve_symbol( dt_strtab, dt_symtab, symtab_offset, name ) { - return Some( pointer ); + let symtab_offset = + ((hasharr as usize) - (chain_zero as usize)) / std::mem::size_of::(); + if let Some(pointer) = + self.resolve_symbol(dt_strtab, dt_symtab, symtab_offset, name) + { + return Some(pointer); } } @@ -211,17 +235,23 @@ impl< 'a > ObjectInfo< 'a > { break; } - hasharr = hasharr.add( 1 ); + hasharr = hasharr.add(1); } None } - unsafe fn dlsym_elf_hash( &self, dt_strtab: *const u8, dt_symtab: *const Symbol, dt_hash: *const u32, name: &[u8] ) -> Option< *mut c_void > { - fn calculate_hash( name: &[u8] ) -> usize { + unsafe fn dlsym_elf_hash( + &self, + dt_strtab: *const u8, + dt_symtab: *const Symbol, + dt_hash: *const u32, + name: &[u8], + ) -> Option<*mut c_void> { + fn calculate_hash(name: &[u8]) -> usize { let mut hash: usize = 0; for &byte in name { - hash = (hash << 4).wrapping_add( byte as usize ); + hash = (hash << 4).wrapping_add(byte as usize); let tmp = hash & 0xf0000000; if tmp != 0 { hash = hash ^ (tmp >> 24); @@ -235,81 +265,94 @@ impl< 'a > ObjectInfo< 'a > { return None; } - - let hash: usize = calculate_hash( name ); - let bucket_idx = *dt_hash.add( 2 + (hash % (*dt_hash as usize)) ) as usize; - let chain: *const u32 = dt_hash.add( 2 + (*dt_hash as usize) + bucket_idx ); - - std::iter::once( bucket_idx as usize ) - .chain( (0..).map( |index| (*chain.add( index )) as usize ).take_while( |&offset| offset != 0 ) ) - .flat_map( |offset| self.resolve_symbol( dt_strtab, dt_symtab, offset, name ) ) + let hash: usize = calculate_hash(name); + let bucket_idx = *dt_hash.add(2 + (hash % (*dt_hash as usize))) as usize; + let chain: *const u32 = dt_hash.add(2 + (*dt_hash as usize) + bucket_idx); + + std::iter::once(bucket_idx as usize) + .chain( + (0..) + .map(|index| (*chain.add(index)) as usize) + .take_while(|&offset| offset != 0), + ) + .flat_map(|offset| self.resolve_symbol(dt_strtab, dt_symtab, offset, name)) .next() } - unsafe fn resolve_symbol( &self, dt_strtab: *const u8, dt_symtab: *const Symbol, symtab_offset: usize, expected_name: &[u8] ) -> Option< *mut c_void > { - let sym = &*dt_symtab.add( symtab_offset ); + unsafe fn resolve_symbol( + &self, + dt_strtab: *const u8, + dt_symtab: *const Symbol, + symtab_offset: usize, + expected_name: &[u8], + ) -> Option<*mut c_void> { + let sym = &*dt_symtab.add(symtab_offset); if sym.st_name == 0 { return None; } - let symbol_name = CStr::from_ptr( dt_strtab.add( sym.st_name as usize ) as *const c_char ).to_bytes(); + let symbol_name = + CStr::from_ptr(dt_strtab.add(sym.st_name as usize) as *const c_char).to_bytes(); if symbol_name != expected_name { return None; } let mut address = (self.address + sym.st_value as usize) as *mut c_void; if sym.st_info & 0b1111 == STT_GNU_IFUNC { - let resolver: extern "C" fn() -> *mut c_void = std::mem::transmute( address ); + let resolver: extern "C" fn() -> *mut c_void = std::mem::transmute(address); address = resolver(); } - Some( address ) + Some(address) } - fn check_address( &self, address: usize ) -> bool { + fn check_address(&self, address: usize) -> bool { let address = address as u64; - self.program_headers.iter().any( |header| { - header.p_type == libc::PT_LOAD && - (address < (header.p_memsz as u64) + (header.p_vaddr as u64) + (self.address as u64)) && - (address >= (header.p_vaddr as u64) + (self.address as u64)) + self.program_headers.iter().any(|header| { + header.p_type == libc::PT_LOAD + && (address + < (header.p_memsz as u64) + (header.p_vaddr as u64) + (self.address as u64)) + && (address >= (header.p_vaddr as u64) + (self.address as u64)) }) } - fn dynamic( &self ) -> impl Iterator< Item = &Dynamic > { - let mut dynamic = self.program_headers.iter() - .find( |header| header.p_type == libc::PT_DYNAMIC ) - .map( |header| (header.p_vaddr as usize + self.address) as *const Dynamic ) - .unwrap_or( std::ptr::null() ); - - std::iter::from_fn( move || - unsafe { - if dynamic.is_null() || (*dynamic).tag == 0 { - return None; - } - - let out = dynamic; - dynamic = dynamic.add( 1 ); - Some( &*out ) + fn dynamic(&self) -> impl Iterator { + let mut dynamic = self + .program_headers + .iter() + .find(|header| header.p_type == libc::PT_DYNAMIC) + .map(|header| (header.p_vaddr as usize + self.address) as *const Dynamic) + .unwrap_or(std::ptr::null()); + + std::iter::from_fn(move || unsafe { + if dynamic.is_null() || (*dynamic).tag == 0 { + return None; } - ) + + let out = dynamic; + dynamic = dynamic.add(1); + Some(&*out) + }) } } #[test] fn test_dlsym() { - let result = ObjectInfo::each( |info| { - if info.name_contains( "libc.so" ) { - assert!( info.dlsym( "gettimeofday" ).is_some() ); - assert!( info.dlsym( "malloc" ).is_some() ); - assert!( info.dlsym( "sleep" ).is_some() ); + let result = ObjectInfo::each(|info| { + if info.name_contains("libc.so") { + assert!(info.dlsym("gettimeofday").is_some()); + assert!(info.dlsym("malloc").is_some()); + assert!(info.dlsym("sleep").is_some()); unsafe { - let strlen: extern "C" fn( *const u8 ) -> usize = std::mem::transmute( info.dlsym( "strlen" ).unwrap() ); - assert_eq!( strlen( b"foobar\0".as_ptr() ), 6 ); + let strlen: extern "C" fn(*const u8) -> usize = + std::mem::transmute(info.dlsym("strlen").unwrap()); + assert_eq!(strlen(b"foobar\0".as_ptr()), 6); - let isalnum: extern "C" fn( u32 ) -> u32 = std::mem::transmute( info.dlsym( "isalnum" ).unwrap() ); + let isalnum: extern "C" fn(u32) -> u32 = + std::mem::transmute(info.dlsym("isalnum").unwrap()); for byte in 0..=255 { - assert_eq!( isalnum( byte as u32 ), libc::isalnum( byte as _ ) as _ ); + assert_eq!(isalnum(byte as u32), libc::isalnum(byte as _) as _); } } @@ -319,5 +362,5 @@ fn test_dlsym() { ControlFlow::Continue(()) }); - assert!( result.is_some() ); + assert!(result.is_some()); } diff --git a/preload/src/event.rs b/preload/src/event.rs index cee93d4a..19864412 100644 --- a/preload/src/event.rs +++ b/preload/src/event.rs @@ -1,9 +1,9 @@ -use std::time::Duration; -use std::sync::Arc; use std::num::NonZeroUsize; +use std::sync::Arc; +use std::time::Duration; -use common::Timestamp; use common::event::AllocationId; +use common::Timestamp; use crate::channel::{Channel, ChannelBuffer}; use crate::global::WeakThreadHandle; @@ -14,15 +14,19 @@ use crate::unwind::Backtrace; pub struct InternalAllocationId { pub thread: u64, pub allocation: u64, - pub checksum: u64 + pub checksum: u64, } impl std::fmt::Display for InternalAllocationId { - fn fmt( &self, fmt: &mut std::fmt::Formatter ) -> std::fmt::Result { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { if self.is_valid() { - write!( fmt, "{{{}, {}}}", self.thread, self.allocation ) + write!(fmt, "{{{}, {}}}", self.thread, self.allocation) } else { - write!( fmt, "{{0x{:X}, 0x{:X}, 0x{:X}}}", self.thread, self.allocation, self.checksum ) + write!( + fmt, + "{{0x{:X}, 0x{:X}, 0x{:X}}}", + self.thread, self.allocation, self.checksum + ) } } } @@ -34,27 +38,27 @@ const UNTRACKED_ALLOCATION: u64 = 0xEBBDDB5F42D04E74; const CHECKSUM_CONSTANT: u64 = 0x8000000000000000; impl InternalAllocationId { - pub const UNTRACKED: Self = Self::new( UNTRACKED_THREAD, UNTRACKED_ALLOCATION ); + pub const UNTRACKED: Self = Self::new(UNTRACKED_THREAD, UNTRACKED_ALLOCATION); - pub const fn new( thread: u64, allocation: u64 ) -> Self { + pub const fn new(thread: u64, allocation: u64) -> Self { InternalAllocationId { thread, allocation, - checksum: thread ^ allocation ^ CHECKSUM_CONSTANT + checksum: thread ^ allocation ^ CHECKSUM_CONSTANT, } } - pub fn is_untracked( self ) -> bool { + pub fn is_untracked(self) -> bool { self == Self::UNTRACKED } - pub fn is_valid( self ) -> bool { + pub fn is_valid(self) -> bool { self.thread ^ self.allocation ^ CHECKSUM_CONSTANT == self.checksum } } -impl From< InternalAllocationId > for common::event::AllocationId { - fn from( id: InternalAllocationId ) -> Self { +impl From for common::event::AllocationId { + fn from(id: InternalAllocationId) -> Self { if id.is_untracked() { common::event::AllocationId::UNTRACKED } else if !id.is_valid() { @@ -62,7 +66,7 @@ impl From< InternalAllocationId > for common::event::AllocationId { } else { common::event::AllocationId { thread: id.thread, - allocation: id.allocation + allocation: id.allocation, } } } @@ -94,13 +98,13 @@ pub(crate) enum InternalEvent { id: AllocationId, timestamp: Timestamp, address: NonZeroUsize, - backtrace: Option< Backtrace >, - tid: u32 + backtrace: Option, + tid: u32, }, Exit, GrabMemoryDump, SetMarker { - value: u32 + value: u32, }, Mallopt { param: i32, @@ -108,37 +112,40 @@ pub(crate) enum InternalEvent { result: i32, backtrace: Backtrace, timestamp: Timestamp, - thread: WeakThreadHandle + thread: WeakThreadHandle, }, OverrideNextTimestamp { - timestamp: Timestamp + timestamp: Timestamp, }, AddressSpaceUpdated { timestamp: Timestamp, maps: String, - new_binaries: Vec< Arc< nwind::BinaryData > > + new_binaries: Vec>, }, - AllocationBucket( crate::allocation_tracker::AllocationBucket ), + AllocationBucket(crate::allocation_tracker::AllocationBucket), } -static EVENT_CHANNEL: Channel< InternalEvent > = Channel::new(); +static EVENT_CHANNEL: Channel = Channel::new(); -pub(crate) fn send_event( event: InternalEvent ) { - EVENT_CHANNEL.send( event ); +pub(crate) fn send_event(event: InternalEvent) { + EVENT_CHANNEL.send(event); } #[inline(always)] -pub(crate) fn send_event_throttled< F: FnOnce() -> InternalEvent >( callback: F ) { - EVENT_CHANNEL.chunked_send_with( callback ); +pub(crate) fn send_event_throttled InternalEvent>(callback: F) { + EVENT_CHANNEL.chunked_send_with(callback); } #[inline(always)] -pub(crate) fn send_event_throttled_sharded< F: FnOnce() -> InternalEvent >( address: usize, callback: F ) { - EVENT_CHANNEL.sharded_chunked_send_with( address, callback ); +pub(crate) fn send_event_throttled_sharded InternalEvent>( + address: usize, + callback: F, +) { + EVENT_CHANNEL.sharded_chunked_send_with(address, callback); } -pub(crate) fn timed_recv_all_events( buffer: &mut ChannelBuffer< InternalEvent >, duration: Duration ) { - EVENT_CHANNEL.timed_recv_all( buffer, duration ) +pub(crate) fn timed_recv_all_events(buffer: &mut ChannelBuffer, duration: Duration) { + EVENT_CHANNEL.timed_recv_all(buffer, duration) } pub(crate) fn flush() { diff --git a/preload/src/global.rs b/preload/src/global.rs index e54081b3..34bd25a9 100644 --- a/preload/src/global.rs +++ b/preload/src/global.rs @@ -1,31 +1,31 @@ use std::cell::UnsafeCell; use std::ops::Deref; -use std::sync::atomic::{AtomicBool, AtomicUsize, AtomicU64, Ordering}; +use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering}; use std::sync::Mutex; use std::thread; +use crate::allocation_tracker::AllocationTracker; use crate::arc_lite::ArcLite; -use crate::event::{InternalAllocationId, InternalEvent, send_event}; +use crate::event::{send_event, InternalAllocationId, InternalEvent}; use crate::spin_lock::{SpinLock, SpinLockGuard}; -use crate::{opt, syscall}; -use crate::unwind::{ThreadUnwindState, prepare_to_start_unwinding}; use crate::timestamp::Timestamp; -use crate::allocation_tracker::AllocationTracker; +use crate::unwind::{prepare_to_start_unwinding, ThreadUnwindState}; +use crate::{opt, syscall}; use thread_local_reentrant::AccessError as TlsAccessError; -pub type RawThreadHandle = ArcLite< ThreadData >; +pub type RawThreadHandle = ArcLite; struct ThreadRegistry { enabled_for_new_threads: bool, - threads_by_system_id: crate::utils::HashMap< u32, RawThreadHandle >, - new_dead_thread_queue: Vec< (Timestamp, RawThreadHandle) >, - thread_counter: u64 + threads_by_system_id: crate::utils::HashMap, + new_dead_thread_queue: Vec<(Timestamp, RawThreadHandle)>, + thread_counter: u64, } unsafe impl Send for ThreadRegistry {} impl ThreadRegistry { - fn threads_by_system_id( &mut self ) -> &mut crate::utils::HashMap< u32, RawThreadHandle > { + fn threads_by_system_id(&mut self) -> &mut crate::utils::HashMap { &mut self.threads_by_system_id } } @@ -39,42 +39,43 @@ const STATE_STARTING: usize = 5; const STATE_ENABLED: usize = 6; const STATE_STOPPING: usize = 7; const STATE_PERMANENTLY_DISABLED: usize = 8; -static STATE: AtomicUsize = AtomicUsize::new( STATE_UNINITIALIZED ); +static STATE: AtomicUsize = AtomicUsize::new(STATE_UNINITIALIZED); -static THREAD_RUNNING: AtomicBool = AtomicBool::new( false ); +static THREAD_RUNNING: AtomicBool = AtomicBool::new(false); const DESIRED_STATE_DISABLED: usize = 0; const DESIRED_STATE_SUSPENDED: usize = 1; const DESIRED_STATE_ENABLED: usize = 2; -static DESIRED_STATE: AtomicUsize = AtomicUsize::new( DESIRED_STATE_DISABLED ); +static DESIRED_STATE: AtomicUsize = AtomicUsize::new(DESIRED_STATE_DISABLED); -static THREAD_REGISTRY: SpinLock< ThreadRegistry > = SpinLock::new( ThreadRegistry { +static THREAD_REGISTRY: SpinLock = SpinLock::new(ThreadRegistry { enabled_for_new_threads: false, threads_by_system_id: crate::utils::empty_hashmap(), new_dead_thread_queue: Vec::new(), - thread_counter: 1 + thread_counter: 1, }); #[inline(never)] -fn lock_thread_registry< R >( callback: impl FnOnce( &mut ThreadRegistry ) -> R ) -> R { - callback( &mut THREAD_REGISTRY.lock() ) +fn lock_thread_registry(callback: impl FnOnce(&mut ThreadRegistry) -> R) -> R { + callback(&mut THREAD_REGISTRY.lock()) } -static PROCESSING_THREAD_HANDLE: SpinLock< Option< libc::pthread_t > > = SpinLock::new( None ); +static PROCESSING_THREAD_HANDLE: SpinLock> = SpinLock::new(None); static mut PROCESSING_THREAD_TID: u32 = 0; -pub static mut SYM_REGISTER_FRAME: Option< unsafe extern "C" fn( fde: *const u8 ) > = None; -pub static mut SYM_DEREGISTER_FRAME: Option< unsafe extern "C" fn( fde: *const u8 ) > = None; +pub static mut SYM_REGISTER_FRAME: Option = None; +pub static mut SYM_DEREGISTER_FRAME: Option = None; -pub static mut INITIAL_TIMESTAMP: Timestamp = Timestamp::from_secs( 0 ); +pub static mut INITIAL_TIMESTAMP: Timestamp = Timestamp::from_secs(0); -static NEXT_MAP_ID: AtomicU64 = AtomicU64::new( 1 ); +static NEXT_MAP_ID: AtomicU64 = AtomicU64::new(1); pub fn next_map_id() -> u64 { - NEXT_MAP_ID.fetch_add( 1, Ordering::Relaxed ) + NEXT_MAP_ID.fetch_add(1, Ordering::Relaxed) } -pub static MMAP_REGISTRY: Mutex< crate::smaps::MapsRegistry > = Mutex::new( crate::smaps::MapsRegistry::new() ); +pub static MMAP_REGISTRY: Mutex = + Mutex::new(crate::smaps::MapsRegistry::new()); static mut PR_SET_VMA_ANON_NAME_SUPPORTED: bool = true; static mut DUMMY_MEMFD: i32 = -1; @@ -96,63 +97,63 @@ pub fn using_unprefixed_jemalloc() -> bool { } #[cfg(not(feature = "jemalloc"))] -static USING_UNPREFIXED_JEMALLOC: AtomicBool = AtomicBool::new( false ); +static USING_UNPREFIXED_JEMALLOC: AtomicBool = AtomicBool::new(false); #[cfg(not(feature = "jemalloc"))] #[inline] pub fn using_unprefixed_jemalloc() -> bool { - USING_UNPREFIXED_JEMALLOC.load( Ordering::Relaxed ) + USING_UNPREFIXED_JEMALLOC.load(Ordering::Relaxed) } pub fn toggle() { - if STATE.load( Ordering::SeqCst ) == STATE_PERMANENTLY_DISABLED { + if STATE.load(Ordering::SeqCst) == STATE_PERMANENTLY_DISABLED { return; } - let value = DESIRED_STATE.load( Ordering::SeqCst ); + let value = DESIRED_STATE.load(Ordering::SeqCst); let new_value = match value { DESIRED_STATE_DISABLED => { - info!( "Tracing will be toggled ON (for the first time)" ); + info!("Tracing will be toggled ON (for the first time)"); DESIRED_STATE_ENABLED - }, + } DESIRED_STATE_SUSPENDED => { - info!( "Tracing will be toggled ON" ); + info!("Tracing will be toggled ON"); DESIRED_STATE_ENABLED - }, + } DESIRED_STATE_ENABLED => { - info!( "Tracing will be toggled OFF" ); + info!("Tracing will be toggled OFF"); DESIRED_STATE_SUSPENDED - }, - _ => unreachable!() + } + _ => unreachable!(), }; - DESIRED_STATE.store( new_value, Ordering::SeqCst ); + DESIRED_STATE.store(new_value, Ordering::SeqCst); } pub fn enable() -> bool { - if STATE.load( Ordering::SeqCst ) == STATE_PERMANENTLY_DISABLED { + if STATE.load(Ordering::SeqCst) == STATE_PERMANENTLY_DISABLED { return false; } - DESIRED_STATE.swap( DESIRED_STATE_ENABLED, Ordering::SeqCst ) != DESIRED_STATE_ENABLED + DESIRED_STATE.swap(DESIRED_STATE_ENABLED, Ordering::SeqCst) != DESIRED_STATE_ENABLED } pub fn disable() -> bool { - if STATE.load( Ordering::SeqCst ) == STATE_PERMANENTLY_DISABLED { + if STATE.load(Ordering::SeqCst) == STATE_PERMANENTLY_DISABLED { return false; } - DESIRED_STATE.swap( DESIRED_STATE_SUSPENDED, Ordering::SeqCst ) == DESIRED_STATE_ENABLED + DESIRED_STATE.swap(DESIRED_STATE_SUSPENDED, Ordering::SeqCst) == DESIRED_STATE_ENABLED } fn is_busy() -> bool { - let state = STATE.load( Ordering::SeqCst ); + let state = STATE.load(Ordering::SeqCst); if state == STATE_STARTING || state == STATE_STOPPING { return true; } - let requested_state = DESIRED_STATE.load( Ordering::SeqCst ); - let is_thread_running = THREAD_RUNNING.load( Ordering::SeqCst ); + let requested_state = DESIRED_STATE.load(Ordering::SeqCst); + let is_thread_running = THREAD_RUNNING.load(Ordering::SeqCst); if requested_state == DESIRED_STATE_DISABLED && is_thread_running && state == STATE_ENABLED { return true; } @@ -162,11 +163,11 @@ fn is_busy() -> bool { fn try_sync_processing_thread_destruction() { let mut handle = PROCESSING_THREAD_HANDLE.lock(); - let state = STATE.load( Ordering::SeqCst ); + let state = STATE.load(Ordering::SeqCst); if state == STATE_STOPPING || state == STATE_DISABLED { - if let Some( handle ) = handle.take() { + if let Some(handle) = handle.take() { unsafe { - libc::pthread_join( handle, std::ptr::null_mut() ); + libc::pthread_join(handle, std::ptr::null_mut()); } } } @@ -176,166 +177,199 @@ pub fn sync() { try_sync_processing_thread_destruction(); while is_busy() { - thread::sleep( std::time::Duration::from_millis( 1 ) ); + thread::sleep(std::time::Duration::from_millis(1)); } try_sync_processing_thread_destruction(); } -pub extern fn on_exit() { - if STATE.load( Ordering::SeqCst ) == STATE_PERMANENTLY_DISABLED { +pub extern "C" fn on_exit() { + if STATE.load(Ordering::SeqCst) == STATE_PERMANENTLY_DISABLED { return; } - info!( "Exit hook called" ); + info!("Exit hook called"); - DESIRED_STATE.store( DESIRED_STATE_DISABLED, Ordering::SeqCst ); - send_event( InternalEvent::Exit ); + DESIRED_STATE.store(DESIRED_STATE_DISABLED, Ordering::SeqCst); + send_event(InternalEvent::Exit); let mut count = 0; - while THREAD_RUNNING.load( Ordering::SeqCst ) == true && count < 2000 { + while THREAD_RUNNING.load(Ordering::SeqCst) == true && count < 2000 { unsafe { - libc::usleep( 25 * 1000 ); + libc::usleep(25 * 1000); count += 1; } } - info!( "Exit hook finished" ); + info!("Exit hook finished"); } -pub unsafe extern fn on_fork() { - STATE.store( STATE_PERMANENTLY_DISABLED, Ordering::SeqCst ); - DESIRED_STATE.store( DESIRED_STATE_DISABLED, Ordering::SeqCst ); - THREAD_RUNNING.store( false, Ordering::SeqCst ); +pub unsafe extern "C" fn on_fork() { + STATE.store(STATE_PERMANENTLY_DISABLED, Ordering::SeqCst); + DESIRED_STATE.store(DESIRED_STATE_DISABLED, Ordering::SeqCst); + THREAD_RUNNING.store(false, Ordering::SeqCst); THREAD_REGISTRY.force_unlock(); // In case we were forked when the lock was held. { let tid = syscall::gettid(); let mut registry = THREAD_REGISTRY.lock(); registry.enabled_for_new_threads = false; - registry.threads_by_system_id().retain( |&_, thread| { - thread.thread_id == tid - }); + registry + .threads_by_system_id() + .retain(|&_, thread| thread.thread_id == tid); } - let _ = TLS.try_with( |tls| tls.set_enabled( false ) ); + let _ = TLS.try_with(|tls| tls.set_enabled(false)); } fn spawn_processing_thread() { - info!( "Will spawn the event processing thread..." ); + info!("Will spawn the event processing thread..."); let mut thread_handle = PROCESSING_THREAD_HANDLE.lock(); - assert!( !THREAD_RUNNING.load( Ordering::SeqCst ) ); + assert!(!THREAD_RUNNING.load(Ordering::SeqCst)); - extern "C" fn thread_main( _: *mut libc::c_void ) -> *mut libc::c_void { - info!( "Processing thread created!" ); + extern "C" fn thread_main(_: *mut libc::c_void) -> *mut libc::c_void { + info!("Processing thread created!"); unsafe { PROCESSING_THREAD_TID = syscall::gettid(); } - THREAD_RUNNING.store( true, Ordering::SeqCst ); + THREAD_RUNNING.store(true, Ordering::SeqCst); - TLS.try_with( |tls| { + TLS.try_with(|tls| { unsafe { *tls.is_internal.get() = true; } - assert!( !tls.is_enabled() ); - }).unwrap(); + assert!(!tls.is_enabled()); + }) + .unwrap(); - let result = std::panic::catch_unwind( || { + let result = std::panic::catch_unwind(|| { crate::processing_thread::thread_main(); }); if result.is_err() { - DESIRED_STATE.store( DESIRED_STATE_DISABLED, Ordering::SeqCst ); + DESIRED_STATE.store(DESIRED_STATE_DISABLED, Ordering::SeqCst); } - lock_thread_registry( |thread_registry| { + lock_thread_registry(|thread_registry| { thread_registry.enabled_for_new_threads = false; for tls in thread_registry.threads_by_system_id().values() { if tls.is_internal() { continue; } - debug!( "Disabling thread {:04x}...", tls.thread_id ); - tls.set_enabled( false ); + debug!("Disabling thread {:04x}...", tls.thread_id); + tls.set_enabled(false); } - STATE.store( STATE_DISABLED, Ordering::SeqCst ); - info!( "Tracing was disabled" ); + STATE.store(STATE_DISABLED, Ordering::SeqCst); + info!("Tracing was disabled"); - THREAD_RUNNING.store( false, Ordering::SeqCst ); + THREAD_RUNNING.store(false, Ordering::SeqCst); }); - if let Err( err ) = result { - std::panic::resume_unwind( err ); + if let Err(err) = result { + std::panic::resume_unwind(err); } std::ptr::null_mut() } - info!( "Creating the event processing thread..." ); + info!("Creating the event processing thread..."); let mut thread: libc::pthread_t; unsafe { thread = std::mem::zeroed(); - if libc::pthread_create( &mut thread, std::ptr::null(), thread_main, std::ptr::null_mut() ) < 0 { - panic!( "failed to start the main memory profiler thread: {}", std::io::Error::last_os_error() ); + if libc::pthread_create( + &mut thread, + std::ptr::null(), + thread_main, + std::ptr::null_mut(), + ) < 0 + { + panic!( + "failed to start the main memory profiler thread: {}", + std::io::Error::last_os_error() + ); } - if libc::pthread_setname_np( thread, b"mem-prof".as_ptr() as *const libc::c_char ) < 0 { - warn!( "Failed to set the name of the processing thread: {}", std::io::Error::last_os_error() ); + if libc::pthread_setname_np(thread, b"mem-prof".as_ptr() as *const libc::c_char) < 0 { + warn!( + "Failed to set the name of the processing thread: {}", + std::io::Error::last_os_error() + ); } } - info!( "Waiting for the event processing thread..." ); - while THREAD_RUNNING.load( Ordering::SeqCst ) == false { + info!("Waiting for the event processing thread..."); + while THREAD_RUNNING.load(Ordering::SeqCst) == false { thread::yield_now(); } - *thread_handle = Some( thread ); - info!( "Event processing thread created!" ); + *thread_handle = Some(thread); + info!("Event processing thread created!"); } #[cfg(target_arch = "x86_64")] -fn find_internal_syms< const N: usize >( names: &[&str; N] ) -> [usize; N] { +fn find_internal_syms(names: &[&str; N]) -> [usize; N] { let mut addresses = [0; N]; unsafe { - use goblin::elf64::header::Header; - use goblin::elf64::section_header::SectionHeader; use goblin::elf::section_header::{SHT_DYNSYM, SHT_SYMTAB}; use goblin::elf::sym::sym64::Sym; + use goblin::elf64::header::Header; + use goblin::elf64::section_header::SectionHeader; let self_path = b"/proc/self/exe\0".as_ptr() as _; - let mut fd = crate::syscall::open_raw_cstr( self_path, libc::O_RDONLY, 0 ); + let mut fd = crate::syscall::open_raw_cstr(self_path, libc::O_RDONLY, 0); if fd < 0 { - warn!( "failed to open /proc/self/exe: {}", std::io::Error::from_raw_os_error( fd ) ); - let path = libc::getauxval( libc::AT_EXECFN ) as *const libc::c_char; + warn!( + "failed to open /proc/self/exe: {}", + std::io::Error::from_raw_os_error(fd) + ); + let path = libc::getauxval(libc::AT_EXECFN) as *const libc::c_char; if !path.is_null() { - fd = crate::syscall::open_raw_cstr( path, libc::O_RDONLY, 0 ); + fd = crate::syscall::open_raw_cstr(path, libc::O_RDONLY, 0); if fd < 0 { - panic!( "failed to open {:?}: {}", std::ffi::CStr::from_ptr( path ), std::io::Error::from_raw_os_error( fd ) ); + panic!( + "failed to open {:?}: {}", + std::ffi::CStr::from_ptr(path), + std::io::Error::from_raw_os_error(fd) + ); } } else { - panic!( "couldn't open /proc/self/exe" ); + panic!("couldn't open /proc/self/exe"); } } let mut buf: libc::stat64 = std::mem::zeroed(); - if libc::fstat64( fd as _, &mut buf ) != 0 { - panic!( "couldn't fstat the executable: {}", std::io::Error::last_os_error() ); + if libc::fstat64(fd as _, &mut buf) != 0 { + panic!( + "couldn't fstat the executable: {}", + std::io::Error::last_os_error() + ); } let size = buf.st_size as usize; - let executable = syscall::mmap( std::ptr::null_mut(), size, libc::PROT_READ, libc::MAP_PRIVATE, fd, 0 ); - assert_ne!( executable, libc::MAP_FAILED ); + let executable = syscall::mmap( + std::ptr::null_mut(), + size, + libc::PROT_READ, + libc::MAP_PRIVATE, + fd, + 0, + ); + assert_ne!(executable, libc::MAP_FAILED); let elf_header = *(executable as *const Header); - let address_offset = libc::getauxval( libc::AT_PHDR ) as usize - elf_header.e_phoff as usize; + let address_offset = libc::getauxval(libc::AT_PHDR) as usize - elf_header.e_phoff as usize; - assert_eq!( elf_header.e_shentsize as usize, std::mem::size_of::< SectionHeader >() ); + assert_eq!( + elf_header.e_shentsize as usize, + std::mem::size_of::() + ); let section_headers = std::slice::from_raw_parts( - ((executable as *const u8).add( elf_header.e_shoff as usize )) as *const SectionHeader, - elf_header.e_shnum as usize + ((executable as *const u8).add(elf_header.e_shoff as usize)) as *const SectionHeader, + elf_header.e_shnum as usize, ); for section_header in section_headers { @@ -343,24 +377,30 @@ fn find_internal_syms< const N: usize >( names: &[&str; N] ) -> [usize; N] { continue; } let strtab_key = section_header.sh_link as usize; - let strtab_section_header = section_headers[ strtab_key ]; - let strtab_bytes = std::slice::from_raw_parts( (executable as *const u8).add( strtab_section_header.sh_offset as usize ), strtab_section_header.sh_size as usize ); + let strtab_section_header = section_headers[strtab_key]; + let strtab_bytes = std::slice::from_raw_parts( + (executable as *const u8).add(strtab_section_header.sh_offset as usize), + strtab_section_header.sh_size as usize, + ); let syms = std::slice::from_raw_parts( - (executable as *const u8).add( section_header.sh_offset as usize ) as *const Sym, - section_header.sh_size as usize / std::mem::size_of::< Sym >() + (executable as *const u8).add(section_header.sh_offset as usize) as *const Sym, + section_header.sh_size as usize / std::mem::size_of::(), ); for sym in syms { - let bytes = &strtab_bytes[ sym.st_name as usize.. ]; - let name = &bytes[ ..bytes.iter().position( |&byte| byte == 0 ).unwrap_or( bytes.len() ) ]; - for (target_name, output_address) in names.iter().zip( addresses.iter_mut() ) { + let bytes = &strtab_bytes[sym.st_name as usize..]; + let name = &bytes[..bytes + .iter() + .position(|&byte| byte == 0) + .unwrap_or(bytes.len())]; + for (target_name, output_address) in names.iter().zip(addresses.iter_mut()) { if *output_address != 0 { continue; } if name == target_name.as_bytes() { - if let Some( address ) = address_offset.checked_add( sym.st_value as usize ) { - info!( "Found '{}' at: 0x{:016X}", target_name, address ); + if let Some(address) = address_offset.checked_add(sym.st_value as usize) { + info!("Found '{}' at: 0x{:016X}", target_name, address); *output_address = address; break; } @@ -369,14 +409,20 @@ fn find_internal_syms< const N: usize >( names: &[&str; N] ) -> [usize; N] { } } - let errcode = syscall::munmap( executable, size ); + let errcode = syscall::munmap(executable, size); if errcode < 0 { - warn!( "munmap failed: {}", std::io::Error::from_raw_os_error( errcode ) ); + warn!( + "munmap failed: {}", + std::io::Error::from_raw_os_error(errcode) + ); } - let errcode = syscall::close( fd ); + let errcode = syscall::close(fd); if errcode < 0 { - warn!( "close failed: {}", std::io::Error::from_raw_os_error( errcode ) ); + warn!( + "close failed: {}", + std::io::Error::from_raw_os_error(errcode) + ); } } @@ -406,7 +452,6 @@ fn hook_jemalloc() { "_rjem_malloc_stats_print", "_rjem_memalign", "_rjem_valloc", - "malloc", "mallocx", "calloc", @@ -451,7 +496,6 @@ fn hook_jemalloc() { crate::api::_rjem_malloc_stats_print as usize, crate::api::_rjem_memalign as usize, crate::api::_rjem_valloc as usize, - // Unprefixed jemalloc. crate::api::_rjem_malloc as usize, crate::api::_rjem_mallocx as usize, @@ -475,47 +519,56 @@ fn hook_jemalloc() { crate::api::_rjem_valloc as usize, ]; - let addresses = find_internal_syms( &names ); - if addresses.iter().all( |&address| address == 0 ) { + let addresses = find_internal_syms(&names); + if addresses.iter().all(|&address| address == 0) { return; } - let index_mallocx = names.iter().position( |name| *name == "mallocx" ).unwrap(); - let index_sdallocx = names.iter().position( |name| *name == "sdallocx" ).unwrap(); - let index_rallocx = names.iter().position( |name| *name == "rallocx" ).unwrap(); + let index_mallocx = names.iter().position(|name| *name == "mallocx").unwrap(); + let index_sdallocx = names.iter().position(|name| *name == "sdallocx").unwrap(); + let index_rallocx = names.iter().position(|name| *name == "rallocx").unwrap(); - let enable_extended_hooks = - addresses[ index_mallocx ] != 0 || - addresses[ index_sdallocx ] != 0 || - addresses[ index_rallocx ] != 0; + let enable_extended_hooks = addresses[index_mallocx] != 0 + || addresses[index_sdallocx] != 0 + || addresses[index_rallocx] != 0; - let extended_hooks_offset = names.iter().position( |name| !name.starts_with( "_rjem_" ) ).unwrap(); + let extended_hooks_offset = names + .iter() + .position(|name| !name.starts_with("_rjem_")) + .unwrap(); if enable_extended_hooks { - info!( "Attaching prefixed jemalloc hooks..." ); - hook_symbols( &names, &addresses, &replacements ); + info!("Attaching prefixed jemalloc hooks..."); + hook_symbols(&names, &addresses, &replacements); } else { - info!( "Attaching unprefixed jemalloc hooks..." ); - hook_symbols( &names[ ..extended_hooks_offset ], &addresses[ ..extended_hooks_offset ], &replacements[ ..extended_hooks_offset ] ); + info!("Attaching unprefixed jemalloc hooks..."); + hook_symbols( + &names[..extended_hooks_offset], + &addresses[..extended_hooks_offset], + &replacements[..extended_hooks_offset], + ); #[cfg(not(feature = "jemalloc"))] - USING_UNPREFIXED_JEMALLOC.store( true, Ordering::SeqCst ); + USING_UNPREFIXED_JEMALLOC.store(true, Ordering::SeqCst); } } #[cfg(target_arch = "x86_64")] -fn hook_symbols( names: &[&str], addresses: &[usize], replacements: &[usize] ) { - assert_eq!( names.len(), replacements.len() ); - assert_eq!( names.len(), addresses.len() ); +fn hook_symbols(names: &[&str], addresses: &[usize], replacements: &[usize]) { + assert_eq!(names.len(), replacements.len()); + assert_eq!(names.len(), addresses.len()); - for ((&name, &replacement), &address) in names.iter().zip( replacements ).zip( addresses ) { + for ((&name, &replacement), &address) in names.iter().zip(replacements).zip(addresses) { if address == 0 { - info!( "Symbol not found: \"{}\"", name ); + info!("Symbol not found: \"{}\"", name); continue; } if replacement == address { - panic!( "tried to replace a symbol with itself: symbol='{}', address=0x{:016X}", name, replacement ); + panic!( + "tried to replace a symbol with itself: symbol='{}', address=0x{:016X}", + name, replacement + ); } let page_1 = address as usize & !(4096 - 1); @@ -524,22 +577,33 @@ fn hook_symbols( names: &[&str], addresses: &[usize], replacements: &[usize] ) { let length = if page_1 == page_2 { 4096 } else { 8192 }; unsafe { - if libc::mprotect( page, length, libc::PROT_READ | libc::PROT_WRITE | libc::PROT_EXEC ) < 0 { - panic!( "mprotect failed: {}", std::io::Error::last_os_error() ); + if libc::mprotect( + page, + length, + libc::PROT_READ | libc::PROT_WRITE | libc::PROT_EXEC, + ) < 0 + { + panic!("mprotect failed: {}", std::io::Error::last_os_error()); } // Write a `jmp` instruction with a RIP-relative addressing mode, with a zero displacement. let mut p = address as *mut u8; - std::ptr::write_unaligned( p, 0xFF ); p = p.add( 1 ); - std::ptr::write_unaligned( p, 0x25 ); p = p.add( 1 ); - std::ptr::write_unaligned( p, 0x00 ); p = p.add( 1 ); - std::ptr::write_unaligned( p, 0x00 ); p = p.add( 1 ); - std::ptr::write_unaligned( p, 0x00 ); p = p.add( 1 ); - std::ptr::write_unaligned( p, 0x00 ); p = p.add( 1 ); - std::ptr::write_unaligned( p as *mut usize, replacement ); - - if libc::mprotect( page, length, libc::PROT_READ | libc::PROT_EXEC ) < 0 { - warn!( "mprotect failed: {}", std::io::Error::last_os_error() ); + std::ptr::write_unaligned(p, 0xFF); + p = p.add(1); + std::ptr::write_unaligned(p, 0x25); + p = p.add(1); + std::ptr::write_unaligned(p, 0x00); + p = p.add(1); + std::ptr::write_unaligned(p, 0x00); + p = p.add(1); + std::ptr::write_unaligned(p, 0x00); + p = p.add(1); + std::ptr::write_unaligned(p, 0x00); + p = p.add(1); + std::ptr::write_unaligned(p as *mut usize, replacement); + + if libc::mprotect(page, length, libc::PROT_READ | libc::PROT_EXEC) < 0 { + warn!("mprotect failed: {}", std::io::Error::last_os_error()); } } } @@ -547,26 +611,32 @@ fn hook_symbols( names: &[&str], addresses: &[usize], replacements: &[usize] ) { fn resolve_original_syms() { unsafe { - let register_frame = libc::dlsym( libc::RTLD_NEXT, b"__register_frame\0".as_ptr() as *const libc::c_char ); - let deregister_frame = libc::dlsym( libc::RTLD_NEXT, b"__deregister_frame\0".as_ptr() as *const libc::c_char ); + let register_frame = libc::dlsym( + libc::RTLD_NEXT, + b"__register_frame\0".as_ptr() as *const libc::c_char, + ); + let deregister_frame = libc::dlsym( + libc::RTLD_NEXT, + b"__deregister_frame\0".as_ptr() as *const libc::c_char, + ); if register_frame.is_null() || deregister_frame.is_null() { if register_frame.is_null() { - warn!( "Failed to find `__register_frame` symbol" ); + warn!("Failed to find `__register_frame` symbol"); } if deregister_frame.is_null() { - warn!( "Failed to find `__deregister_frame` symbol" ); + warn!("Failed to find `__deregister_frame` symbol"); } return; } - crate::global::SYM_REGISTER_FRAME = Some( std::mem::transmute( register_frame ) ); - crate::global::SYM_DEREGISTER_FRAME = Some( std::mem::transmute( deregister_frame ) ); + crate::global::SYM_REGISTER_FRAME = Some(std::mem::transmute(register_frame)); + crate::global::SYM_DEREGISTER_FRAME = Some(std::mem::transmute(deregister_frame)); } } fn check_set_vma_anon_name() { if crate::opt::get().disable_pr_set_vma_anon_name { - warn!( "PR_SET_VMA_ANON_NAME forcibly disabled!" ); + warn!("PR_SET_VMA_ANON_NAME forcibly disabled!"); unsafe { PR_SET_VMA_ANON_NAME_SUPPORTED = false; } @@ -575,11 +645,18 @@ fn check_set_vma_anon_name() { } unsafe { - let pointer = crate::syscall::mmap( std::ptr::null_mut(), 4096, 0, libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, -1, 0 ); - assert_ne!( pointer, libc::MAP_FAILED ); + let pointer = crate::syscall::mmap( + std::ptr::null_mut(), + 4096, + 0, + libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, + -1, + 0, + ); + assert_ne!(pointer, libc::MAP_FAILED); - let is_supported = crate::syscall::pr_set_vma_anon_name( pointer, 4096, b"test\0" ); - crate::syscall::munmap( pointer, 4096 ); + let is_supported = crate::syscall::pr_set_vma_anon_name(pointer, 4096, b"test\0"); + crate::syscall::munmap(pointer, 4096); if !is_supported { warn!( "PR_SET_VMA_ANON_NAME is not supported (Linux 5.17+ required); will try to emulate in userspace" ); @@ -594,7 +671,7 @@ fn initialize_stage_1() { } crate::init::initialize_logger(); - info!( "Version: {}", env!( "CARGO_PKG_VERSION" ) ); + info!("Version: {}", env!("CARGO_PKG_VERSION")); unsafe { crate::opt::initialize(); @@ -603,13 +680,13 @@ fn initialize_stage_1() { check_set_vma_anon_name(); if !is_pr_set_vma_anon_name_supported() { unsafe { - let fd = libc::memfd_create( b"bytehound_padding\0".as_ptr().cast(), libc::MFD_CLOEXEC ); + let fd = libc::memfd_create(b"bytehound_padding\0".as_ptr().cast(), libc::MFD_CLOEXEC); if fd < 0 { - error!( "Failed to create a memfd for a dummy map!" ); + error!("Failed to create a memfd for a dummy map!"); libc::abort(); } - info!( "Dummy memfd created: fd = {}", fd ); + info!("Dummy memfd created: fd = {}", fd); DUMMY_MEMFD = fd; } } @@ -624,7 +701,7 @@ fn initialize_stage_1() { #[cfg(target_arch = "x86_64")] hook_private_mmap(); - info!( "Stage 1 initialization finished" ); + info!("Stage 1 initialization finished"); } #[cfg(target_arch = "x86_64")] @@ -633,13 +710,12 @@ fn hook_private_mmap() { let mut address_mmap = std::ptr::null_mut(); let mut address_munmap = std::ptr::null_mut(); - crate::elf::ObjectInfo::each( |info| { - if info.name_contains( "libc.so" ) { - if let Some( address ) = info.dlsym( "__mmap" ) { + crate::elf::ObjectInfo::each(|info| { + if info.name_contains("libc.so") { + if let Some(address) = info.dlsym("__mmap") { address_mmap = address; - } - if let Some( address ) = info.dlsym( "__munmap" ) { + if let Some(address) = info.dlsym("__munmap") { address_munmap = address; } @@ -650,48 +726,60 @@ fn hook_private_mmap() { }); if !address_mmap.is_null() { - info!( "Found __mmap at: 0x{:016X}", address_mmap as usize ); + info!("Found __mmap at: 0x{:016X}", address_mmap as usize); } if !address_munmap.is_null() { - info!( "Found __munmap at: 0x{:016X}", address_munmap as usize ); + info!("Found __munmap at: 0x{:016X}", address_munmap as usize); } - hook_symbols( &["__mmap", "__munmap"], &[address_mmap as usize, address_munmap as usize], &[crate::api::__mmap as usize, crate::api::__munmap as usize] ); + hook_symbols( + &["__mmap", "__munmap"], + &[address_mmap as usize, address_munmap as usize], + &[crate::api::__mmap as usize, crate::api::__munmap as usize], + ); } fn initialize_stage_2() { - info!( "Initializing stage 2..." ); + info!("Initializing stage 2..."); crate::init::initialize_atexit_hook(); crate::init::initialize_signal_handlers(); if !opt::get().track_child_processes { - std::env::remove_var( "LD_PRELOAD" ); + std::env::remove_var("LD_PRELOAD"); } - info!( "Stage 2 initialization finished" ); + info!("Stage 2 initialization finished"); } -static ALLOW_STAGE_2: AtomicBool = AtomicBool::new( false ); +static ALLOW_STAGE_2: AtomicBool = AtomicBool::new(false); #[used] #[link_section = ".init_array.00099"] -static INIT_ARRAY: unsafe extern "C" fn( libc::c_int, *mut *mut u8, *mut *mut u8 ) = { - unsafe extern "C" fn function( _argc: libc::c_int, _argv: *mut *mut u8, _envp: *mut *mut u8 ) { - ALLOW_STAGE_2.store( true, Ordering::SeqCst ); - try_enable( STATE.load( Ordering::Relaxed ) ); +static INIT_ARRAY: unsafe extern "C" fn(libc::c_int, *mut *mut u8, *mut *mut u8) = { + unsafe extern "C" fn function(_argc: libc::c_int, _argv: *mut *mut u8, _envp: *mut *mut u8) { + ALLOW_STAGE_2.store(true, Ordering::SeqCst); + try_enable(STATE.load(Ordering::Relaxed)); } function }; #[cold] #[inline(never)] -fn try_enable( mut state: usize ) -> bool { +fn try_enable(mut state: usize) -> bool { if state == STATE_UNINITIALIZED { - if STATE.compare_exchange( STATE_UNINITIALIZED, STATE_INITIALIZING_STAGE_1, Ordering::SeqCst, Ordering::SeqCst ).is_ok() { + if STATE + .compare_exchange( + STATE_UNINITIALIZED, + STATE_INITIALIZING_STAGE_1, + Ordering::SeqCst, + Ordering::SeqCst, + ) + .is_ok() + { initialize_stage_1(); - STATE.store( STATE_PARTIALLY_INITIALIZED, Ordering::SeqCst ); + STATE.store(STATE_PARTIALLY_INITIALIZED, Ordering::SeqCst); state = STATE_PARTIALLY_INITIALIZED; } else { return false; @@ -699,42 +787,58 @@ fn try_enable( mut state: usize ) -> bool { } if state == STATE_PARTIALLY_INITIALIZED { - if !ALLOW_STAGE_2.load( Ordering::SeqCst ) { + if !ALLOW_STAGE_2.load(Ordering::SeqCst) { return false; } - if STATE.compare_exchange( STATE_PARTIALLY_INITIALIZED, STATE_INITIALIZING_STAGE_2, Ordering::SeqCst, Ordering::SeqCst ).is_ok() { + if STATE + .compare_exchange( + STATE_PARTIALLY_INITIALIZED, + STATE_INITIALIZING_STAGE_2, + Ordering::SeqCst, + Ordering::SeqCst, + ) + .is_ok() + { initialize_stage_2(); - STATE.store( STATE_DISABLED, Ordering::SeqCst ); + STATE.store(STATE_DISABLED, Ordering::SeqCst); } else { return false; } } - if DESIRED_STATE.load( Ordering::SeqCst ) == DESIRED_STATE_DISABLED { + if DESIRED_STATE.load(Ordering::SeqCst) == DESIRED_STATE_DISABLED { return false; } - if STATE.compare_exchange( STATE_DISABLED, STATE_STARTING, Ordering::SeqCst, Ordering::SeqCst ).is_err() { + if STATE + .compare_exchange( + STATE_DISABLED, + STATE_STARTING, + Ordering::SeqCst, + Ordering::SeqCst, + ) + .is_err() + { return false; } - lock_thread_registry( |thread_registry| { - assert!( !thread_registry.enabled_for_new_threads ); + lock_thread_registry(|thread_registry| { + assert!(!thread_registry.enabled_for_new_threads); }); prepare_to_start_unwinding(); spawn_processing_thread(); - lock_thread_registry( |thread_registry| { + lock_thread_registry(|thread_registry| { thread_registry.enabled_for_new_threads = true; for tls in thread_registry.threads_by_system_id().values() { if tls.is_internal() { continue; } - debug!( "Enabling thread {:04x}...", tls.thread_id ); - tls.set_enabled( true ); + debug!("Enabling thread {:04x}...", tls.thread_id); + tls.set_enabled(true); } }); @@ -742,51 +846,59 @@ fn try_enable( mut state: usize ) -> bool { crate::allocation_tracker::initialize(); - STATE.store( STATE_ENABLED, Ordering::SeqCst ); - info!( "Tracing was enabled" ); + STATE.store(STATE_ENABLED, Ordering::SeqCst); + info!("Tracing was enabled"); true } pub fn try_disable_if_requested() { - if DESIRED_STATE.load( Ordering::SeqCst ) != DESIRED_STATE_DISABLED { + if DESIRED_STATE.load(Ordering::SeqCst) != DESIRED_STATE_DISABLED { return; } - if STATE.compare_exchange( STATE_ENABLED, STATE_STOPPING, Ordering::SeqCst, Ordering::SeqCst ).is_err() { + if STATE + .compare_exchange( + STATE_ENABLED, + STATE_STOPPING, + Ordering::SeqCst, + Ordering::SeqCst, + ) + .is_err() + { return; } - send_event( InternalEvent::Exit ); + send_event(InternalEvent::Exit); } const THROTTLE_LIMIT: usize = 8192; #[cold] #[inline(never)] -fn throttle( tls: &RawThreadHandle ) { - while ArcLite::get_refcount_relaxed( tls ) >= THROTTLE_LIMIT { +fn throttle(tls: &RawThreadHandle) { + while ArcLite::get_refcount_relaxed(tls) >= THROTTLE_LIMIT { thread::yield_now(); } } pub fn is_actively_running() -> bool { - DESIRED_STATE.load( Ordering::Relaxed ) == DESIRED_STATE_ENABLED + DESIRED_STATE.load(Ordering::Relaxed) == DESIRED_STATE_ENABLED } /// A handle to per-thread storage; you can't do anything with it. /// /// Can be sent to other threads. -pub struct WeakThreadHandle( RawThreadHandle ); +pub struct WeakThreadHandle(RawThreadHandle); unsafe impl Send for WeakThreadHandle {} unsafe impl Sync for WeakThreadHandle {} impl WeakThreadHandle { - pub fn system_tid( &self ) -> u32 { + pub fn system_tid(&self) -> u32 { self.0.thread_id } - pub fn unwind_state( &self ) -> (&UnsafeCell< bool >, &UnsafeCell< ThreadUnwindState >) { + pub fn unwind_state(&self) -> (&UnsafeCell, &UnsafeCell) { (&self.0.is_unwinding, &self.0.unwind_state) } } @@ -794,66 +906,62 @@ impl WeakThreadHandle { /// A handle to per-thread storage. /// /// Can only be aquired for the current thread, and cannot be sent to other threads. -pub struct StrongThreadHandle( Option< RawThreadHandle > ); +pub struct StrongThreadHandle(Option); impl StrongThreadHandle { #[inline(always)] - pub fn acquire() -> Option< Self > { - let state = STATE.load( Ordering::Relaxed ); + pub fn acquire() -> Option { + let state = STATE.load(Ordering::Relaxed); if state != STATE_ENABLED { - if !try_enable( state ) { + if !try_enable(state) { return None; } } - let tls = TLS.try_with( |tls| { + let tls = TLS.try_with(|tls| { if !tls.is_enabled() { None } else { - if ArcLite::get_refcount_relaxed( tls ) >= THROTTLE_LIMIT { - throttle( tls ); + if ArcLite::get_refcount_relaxed(tls) >= THROTTLE_LIMIT { + throttle(tls); } - tls.set_enabled( false ); - Some( tls.0.clone() ) + tls.set_enabled(false); + Some(tls.0.clone()) } }); match tls { - Ok( Some( tls ) ) => { - Some( StrongThreadHandle( Some( tls ) ) ) - }, - Ok( None ) | Err( TlsAccessError::Uninitialized ) => { - None - }, - Err( TlsAccessError::Destroyed ) => { - acquire_slow().map( |tls| StrongThreadHandle( Some( tls ) ) ) + Ok(Some(tls)) => Some(StrongThreadHandle(Some(tls))), + Ok(None) | Err(TlsAccessError::Uninitialized) => None, + Err(TlsAccessError::Destroyed) => { + acquire_slow().map(|tls| StrongThreadHandle(Some(tls))) } } } - pub fn decay( mut self ) -> WeakThreadHandle { + pub fn decay(mut self) -> WeakThreadHandle { let tls = match self.0.take() { - Some( tls ) => tls, - None => unsafe { std::hint::unreachable_unchecked() } + Some(tls) => tls, + None => unsafe { std::hint::unreachable_unchecked() }, }; - tls.set_enabled( true ); - WeakThreadHandle( tls ) + tls.set_enabled(true); + WeakThreadHandle(tls) } - pub fn unwind_state( &mut self ) -> (&UnsafeCell< bool >, &UnsafeCell< ThreadUnwindState >) { + pub fn unwind_state(&mut self) -> (&UnsafeCell, &UnsafeCell) { let tls = match self.0.as_ref() { - Some( tls ) => tls, - None => unsafe { std::hint::unreachable_unchecked() } + Some(tls) => tls, + None => unsafe { std::hint::unreachable_unchecked() }, }; (&tls.is_unwinding, &tls.unwind_state) } - pub fn on_new_allocation( &mut self ) -> InternalAllocationId { + pub fn on_new_allocation(&mut self) -> InternalAllocationId { let tls = match self.0.as_ref() { - Some( tls ) => tls, - None => unsafe { std::hint::unreachable_unchecked() } + Some(tls) => tls, + None => unsafe { std::hint::unreachable_unchecked() }, }; let counter = tls.allocation_counter.get(); @@ -863,104 +971,109 @@ impl StrongThreadHandle { *counter += 1; } - InternalAllocationId::new( tls.internal_thread_id, allocation ) + InternalAllocationId::new(tls.internal_thread_id, allocation) } - pub fn system_tid( &self ) -> u32 { + pub fn system_tid(&self) -> u32 { let tls = match self.0.as_ref() { - Some( tls ) => tls, - None => unsafe { std::hint::unreachable_unchecked() } + Some(tls) => tls, + None => unsafe { std::hint::unreachable_unchecked() }, }; tls.thread_id } - pub fn unique_tid( &self ) -> u64 { + pub fn unique_tid(&self) -> u64 { let tls = match self.0.as_ref() { - Some( tls ) => tls, - None => unsafe { std::hint::unreachable_unchecked() } + Some(tls) => tls, + None => unsafe { std::hint::unreachable_unchecked() }, }; tls.internal_thread_id } - pub fn allocation_tracker( &self ) -> &AllocationTracker { + pub fn allocation_tracker(&self) -> &AllocationTracker { let tls = match self.0.as_ref() { - Some( tls ) => tls, - None => unsafe { std::hint::unreachable_unchecked() } + Some(tls) => tls, + None => unsafe { std::hint::unreachable_unchecked() }, }; &tls.allocation_tracker } - pub(crate) fn zombie_events( &self ) -> &SpinLock< Vec< InternalEvent > > { + pub(crate) fn zombie_events(&self) -> &SpinLock> { let tls = match self.0.as_ref() { - Some( tls ) => tls, - None => unsafe { std::hint::unreachable_unchecked() } + Some(tls) => tls, + None => unsafe { std::hint::unreachable_unchecked() }, }; &tls.zombie_events } - pub fn is_dead( &self ) -> bool { + pub fn is_dead(&self) -> bool { let tls = match self.0.as_ref() { - Some( tls ) => tls, - None => unsafe { std::hint::unreachable_unchecked() } + Some(tls) => tls, + None => unsafe { std::hint::unreachable_unchecked() }, }; - tls.is_dead.load( Ordering::Relaxed ) + tls.is_dead.load(Ordering::Relaxed) } } impl Drop for StrongThreadHandle { - fn drop( &mut self ) { - if let Some( tls ) = self.0.take() { - tls.set_enabled( true ); + fn drop(&mut self) { + if let Some(tls) = self.0.take() { + tls.set_enabled(true); } } } pub enum ThreadHandleKind { - Strong( StrongThreadHandle ), - Weak( WeakThreadHandle ) + Strong(StrongThreadHandle), + Weak(WeakThreadHandle), } impl ThreadHandleKind { - pub fn system_tid( &self ) -> u32 { + pub fn system_tid(&self) -> u32 { match self { - ThreadHandleKind::Strong( StrongThreadHandle( ref handle ) ) => handle.as_ref().unwrap(), - ThreadHandleKind::Weak( WeakThreadHandle( ref handle ) ) => handle, - }.thread_id + ThreadHandleKind::Strong(StrongThreadHandle(ref handle)) => handle.as_ref().unwrap(), + ThreadHandleKind::Weak(WeakThreadHandle(ref handle)) => handle, + } + .thread_id } } #[cold] #[inline(never)] -fn acquire_slow() -> Option< RawThreadHandle > { +fn acquire_slow() -> Option { let current_thread_id = syscall::gettid(); - lock_thread_registry( |thread_registry| { - if let Some( thread ) = thread_registry.threads_by_system_id().get( ¤t_thread_id ) { - debug!( "Acquired a dead thread: {:04X}", current_thread_id ); - Some( thread.clone() ) + lock_thread_registry(|thread_registry| { + if let Some(thread) = thread_registry + .threads_by_system_id() + .get(¤t_thread_id) + { + debug!("Acquired a dead thread: {:04X}", current_thread_id); + Some(thread.clone()) } else { - warn!( "Failed to acquire a handle for thread: {:04X}", current_thread_id ); + warn!( + "Failed to acquire a handle for thread: {:04X}", + current_thread_id + ); None } }) } #[inline(always)] -pub fn acquire_any_thread_handle() -> Option< ThreadHandleKind > { - let mut state = STATE.load( Ordering::Relaxed ); +pub fn acquire_any_thread_handle() -> Option { + let mut state = STATE.load(Ordering::Relaxed); if state != STATE_ENABLED { - if !try_enable( state ) { - state = STATE.load( Ordering::Relaxed ); + if !try_enable(state) { + state = STATE.load(Ordering::Relaxed); match state { - | STATE_UNINITIALIZED - | STATE_INITIALIZING_STAGE_1 - => return None, + STATE_UNINITIALIZED | STATE_INITIALIZING_STAGE_1 => return None, _ => { - if DESIRED_STATE.load( Ordering::SeqCst ) != DESIRED_STATE_ENABLED { + if DESIRED_STATE.load(Ordering::SeqCst) != DESIRED_STATE_ENABLED { return None; } } @@ -968,35 +1081,31 @@ pub fn acquire_any_thread_handle() -> Option< ThreadHandleKind > { } } - let tls = TLS.try_with( |tls| { + let tls = TLS.try_with(|tls| { if !tls.is_enabled() { - ThreadHandleKind::Weak( WeakThreadHandle( tls.0.clone() ) ) + ThreadHandleKind::Weak(WeakThreadHandle(tls.0.clone())) } else { - if ArcLite::get_refcount_relaxed( tls ) >= THROTTLE_LIMIT { - throttle( tls ); + if ArcLite::get_refcount_relaxed(tls) >= THROTTLE_LIMIT { + throttle(tls); } - tls.set_enabled( false ); - ThreadHandleKind::Strong( StrongThreadHandle( Some( tls.0.clone() ) ) ) + tls.set_enabled(false); + ThreadHandleKind::Strong(StrongThreadHandle(Some(tls.0.clone()))) } }); match tls { - Ok( tls ) => { - Some( tls ) - }, - Err( TlsAccessError::Uninitialized ) => { - None - }, - Err( TlsAccessError::Destroyed ) => { - Some( ThreadHandleKind::Strong( StrongThreadHandle( Some( acquire_slow()? ) ) ) ) - } + Ok(tls) => Some(tls), + Err(TlsAccessError::Uninitialized) => None, + Err(TlsAccessError::Destroyed) => Some(ThreadHandleKind::Strong(StrongThreadHandle(Some( + acquire_slow()?, + )))), } } pub struct AllocationLock { current_thread_id: u32, - registry_lock: SpinLockGuard< 'static, ThreadRegistry > + registry_lock: SpinLockGuard<'static, ThreadRegistry>, } impl AllocationLock { @@ -1013,11 +1122,11 @@ impl AllocationLock { continue; } unsafe { - ArcLite::add( tls, THROTTLE_LIMIT ); + ArcLite::add(tls, THROTTLE_LIMIT); } } - std::sync::atomic::fence( Ordering::SeqCst ); + std::sync::atomic::fence(Ordering::SeqCst); for (&thread_id, tls) in threads.iter_mut() { if thread_id == current_thread_id { @@ -1027,29 +1136,29 @@ impl AllocationLock { if tls.is_internal() { continue; } - while ArcLite::get_refcount_relaxed( tls ) != THROTTLE_LIMIT { + while ArcLite::get_refcount_relaxed(tls) != THROTTLE_LIMIT { thread::yield_now(); } } - std::sync::atomic::fence( Ordering::SeqCst ); + std::sync::atomic::fence(Ordering::SeqCst); AllocationLock { current_thread_id, - registry_lock + registry_lock, } } } impl Drop for AllocationLock { - fn drop( &mut self ) { + fn drop(&mut self) { for (&thread_id, tls) in self.registry_lock.threads_by_system_id().iter_mut() { if thread_id == self.current_thread_id { continue; } unsafe { - ArcLite::sub( tls, THROTTLE_LIMIT ); + ArcLite::sub(tls, THROTTLE_LIMIT); } } } @@ -1058,59 +1167,59 @@ impl Drop for AllocationLock { pub struct ThreadData { thread_id: u32, internal_thread_id: u64, - is_internal: UnsafeCell< bool >, + is_internal: UnsafeCell, enabled: AtomicBool, is_dead: AtomicBool, - is_unwinding: UnsafeCell< bool >, - unwind_state: UnsafeCell< ThreadUnwindState >, - allocation_counter: UnsafeCell< u64 >, + is_unwinding: UnsafeCell, + unwind_state: UnsafeCell, + allocation_counter: UnsafeCell, allocation_tracker: AllocationTracker, - zombie_events: SpinLock< Vec< InternalEvent > > + zombie_events: SpinLock>, } impl ThreadData { #[inline(always)] - pub fn is_enabled( &self ) -> bool { - self.enabled.load( Ordering::Relaxed ) + pub fn is_enabled(&self) -> bool { + self.enabled.load(Ordering::Relaxed) } #[inline(always)] - pub fn is_internal( &self ) -> bool { - unsafe { - *self.is_internal.get() || PROCESSING_THREAD_TID == self.thread_id - } + pub fn is_internal(&self) -> bool { + unsafe { *self.is_internal.get() || PROCESSING_THREAD_TID == self.thread_id } } - fn set_enabled( &self, value: bool ) { - self.enabled.store( value, Ordering::Relaxed ) + fn set_enabled(&self, value: bool) { + self.enabled.store(value, Ordering::Relaxed) } } -struct ThreadSentinel( RawThreadHandle ); +struct ThreadSentinel(RawThreadHandle); impl Deref for ThreadSentinel { type Target = RawThreadHandle; - fn deref( &self ) -> &Self::Target { + fn deref(&self) -> &Self::Target { &self.0 } } impl Drop for ThreadSentinel { - fn drop( &mut self ) { - self.is_dead.store( true, Ordering::SeqCst ); + fn drop(&mut self) { + self.is_dead.store(true, Ordering::SeqCst); - let is_enabled = self.enabled.load( Ordering::SeqCst ); - self.enabled.store( false, Ordering::SeqCst ); + let is_enabled = self.enabled.load(Ordering::SeqCst); + self.enabled.store(false, Ordering::SeqCst); - lock_thread_registry( |thread_registry| { - if let Some( thread ) = thread_registry.threads_by_system_id().get( &self.thread_id ) { + lock_thread_registry(|thread_registry| { + if let Some(thread) = thread_registry.threads_by_system_id().get(&self.thread_id) { let thread = thread.clone(); - thread_registry.new_dead_thread_queue.push( (crate::timestamp::get_timestamp(), thread) ); + thread_registry + .new_dead_thread_queue + .push((crate::timestamp::get_timestamp(), thread)); } }); - debug!( "Thread dropped: {:04X}", self.thread_id ); - self.enabled.store( is_enabled, Ordering::SeqCst ); + debug!("Thread dropped: {:04X}", self.thread_id); + self.enabled.store(is_enabled, Ordering::SeqCst); } } @@ -1146,42 +1255,50 @@ thread_local_reentrant! { #[derive(Default)] pub struct ThreadGarbageCollector { - buffer: Vec< (Timestamp, RawThreadHandle) >, - dead_threads: Vec< (Timestamp, RawThreadHandle) >, + buffer: Vec<(Timestamp, RawThreadHandle)>, + dead_threads: Vec<(Timestamp, RawThreadHandle)>, } impl ThreadGarbageCollector { - pub(crate) fn run( &mut self, now: Timestamp, events: &mut crate::channel::ChannelBuffer< InternalEvent > ) { + pub(crate) fn run( + &mut self, + now: Timestamp, + events: &mut crate::channel::ChannelBuffer, + ) { use crate::utils::Entry; - lock_thread_registry( |thread_registry| { - std::mem::swap( &mut thread_registry.new_dead_thread_queue, &mut self.buffer ); + lock_thread_registry(|thread_registry| { + std::mem::swap(&mut thread_registry.new_dead_thread_queue, &mut self.buffer); }); - for (timestamp, thread) in self.buffer.drain( .. ) { - crate::allocation_tracker::on_thread_destroyed( thread.internal_thread_id ); - events.extend( thread.zombie_events.lock().drain( .. ) ); - self.dead_threads.push( (timestamp, thread) ); + for (timestamp, thread) in self.buffer.drain(..) { + crate::allocation_tracker::on_thread_destroyed(thread.internal_thread_id); + events.extend(thread.zombie_events.lock().drain(..)); + self.dead_threads.push((timestamp, thread)); } if self.dead_threads.is_empty() { return; } - let count = self.dead_threads.iter() - .take_while( |&(time_of_death, _)| time_of_death.as_secs() + 3 < now.as_secs() ) + let count = self + .dead_threads + .iter() + .take_while(|&(time_of_death, _)| time_of_death.as_secs() + 3 < now.as_secs()) .count(); if count == 0 { return; } - for (_, thread) in self.dead_threads.drain( ..count ) { - lock_thread_registry( |thread_registry| { + for (_, thread) in self.dead_threads.drain(..count) { + lock_thread_registry(|thread_registry| { let mut entry_by_system_id = None; - if let Entry::Occupied( entry ) = thread_registry.threads_by_system_id.entry( thread.thread_id ) { - if RawThreadHandle::ptr_eq( entry.get(), &thread ) { - entry_by_system_id = Some( entry.remove_entry() ); + if let Entry::Occupied(entry) = + thread_registry.threads_by_system_id.entry(thread.thread_id) + { + if RawThreadHandle::ptr_eq(entry.get(), &thread) { + entry_by_system_id = Some(entry.remove_entry()); } } diff --git a/preload/src/init.rs b/preload/src/init.rs index 2b8d0359..9394a7c0 100644 --- a/preload/src/init.rs +++ b/preload/src/init.rs @@ -7,14 +7,14 @@ pub static mut SYSCALL_LOGGER: logger::SyscallLogger = logger::SyscallLogger::em pub static mut FILE_LOGGER: logger::FileLogger = logger::FileLogger::empty(); pub fn initialize_logger() { - let log_level = if let Some( value ) = unsafe { crate::syscall::getenv( b"MEMORY_PROFILER_LOG" ) } { + let log_level = if let Some(value) = unsafe { crate::syscall::getenv(b"MEMORY_PROFILER_LOG") } { match value.to_str() { - Some( "trace" ) => log::LevelFilter::Trace, - Some( "debug" ) => log::LevelFilter::Debug, - Some( "info" ) => log::LevelFilter::Info, - Some( "warn" ) => log::LevelFilter::Warn, - Some( "error" ) => log::LevelFilter::Error, - _ => log::LevelFilter::Off + Some("trace") => log::LevelFilter::Trace, + Some("debug") => log::LevelFilter::Debug, + Some("info") => log::LevelFilter::Info, + Some("warn") => log::LevelFilter::Warn, + Some("error") => log::LevelFilter::Error, + _ => log::LevelFilter::Off, } } else { log::LevelFilter::Off @@ -22,60 +22,65 @@ pub fn initialize_logger() { let pid = crate::syscall::getpid(); unsafe { - SYSCALL_LOGGER.initialize( log_level, pid ); + SYSCALL_LOGGER.initialize(log_level, pid); } - if let Some( value ) = unsafe { crate::syscall::getenv( b"MEMORY_PROFILER_LOGFILE" ) } { - let path = generate_filename( value.as_slice(), None ); - let rotate_at = unsafe { crate::syscall::getenv( b"MEMORY_PROFILER_LOGFILE_ROTATE_WHEN_BIGGER_THAN" ) }.and_then( |value| value.to_str()?.parse().ok() ); + if let Some(value) = unsafe { crate::syscall::getenv(b"MEMORY_PROFILER_LOGFILE") } { + let path = generate_filename(value.as_slice(), None); + let rotate_at = + unsafe { crate::syscall::getenv(b"MEMORY_PROFILER_LOGFILE_ROTATE_WHEN_BIGGER_THAN") } + .and_then(|value| value.to_str()?.parse().ok()); unsafe { - if let Ok(()) = FILE_LOGGER.initialize( path, rotate_at, log_level, pid ) { - log::set_logger( &FILE_LOGGER ).unwrap(); + if let Ok(()) = FILE_LOGGER.initialize(path, rotate_at, log_level, pid) { + log::set_logger(&FILE_LOGGER).unwrap(); } } } else { unsafe { - log::set_logger( &SYSCALL_LOGGER ).unwrap(); + log::set_logger(&SYSCALL_LOGGER).unwrap(); } } - log::set_max_level( log_level ); + log::set_max_level(log_level); } pub fn initialize_atexit_hook() { - info!( "Setting atexit hook..." ); + info!("Setting atexit hook..."); unsafe { - let result = libc::atexit( on_exit ); + let result = libc::atexit(on_exit); if result != 0 { - error!( "Cannot set the at-exit hook" ); + error!("Cannot set the at-exit hook"); } } } pub fn initialize_signal_handlers() { - extern "C" fn sigusr_handler( signal: libc::c_int ) { + extern "C" fn sigusr_handler(signal: libc::c_int) { let signal_name = match signal { libc::SIGUSR1 => "SIGUSR1", libc::SIGUSR2 => "SIGUSR2", - _ => "???" + _ => "???", }; - info!( "Signal handler triggered with signal: {} ({})", signal_name, signal ); + info!( + "Signal handler triggered with signal: {} ({})", + signal_name, signal + ); crate::global::toggle(); } if opt::get().register_sigusr1 { - info!( "Registering SIGUSR1 handler..." ); + info!("Registering SIGUSR1 handler..."); unsafe { - libc::signal( libc::SIGUSR1, sigusr_handler as libc::sighandler_t ); + libc::signal(libc::SIGUSR1, sigusr_handler as libc::sighandler_t); } } if opt::get().register_sigusr2 { - info!( "Registering SIGUSR2 handler..." ); + info!("Registering SIGUSR2 handler..."); unsafe { - libc::signal( libc::SIGUSR2, sigusr_handler as libc::sighandler_t ); + libc::signal(libc::SIGUSR2, sigusr_handler as libc::sighandler_t); } } } diff --git a/preload/src/lib.rs b/preload/src/lib.rs index 71c637ff..4b472bd6 100644 --- a/preload/src/lib.rs +++ b/preload/src/lib.rs @@ -17,29 +17,29 @@ use std::os::unix::ffi::OsStrExt; #[macro_use] mod macros; -mod unwind; -mod timestamp; -mod spin_lock; -mod channel; -mod utils; -mod arch; -mod logger; -mod opt; -mod syscall; -mod raw_file; -mod arc_lite; -mod writers; -mod writer_memory; +mod allocation_tracker; mod api; +mod arc_lite; +mod arch; +mod channel; +mod elf; mod event; -mod init; -mod processing_thread; mod global; -mod ordered_map; +mod init; +mod logger; mod nohash; -mod allocation_tracker; +mod opt; +mod ordered_map; +mod processing_thread; +mod raw_file; mod smaps; -mod elf; +mod spin_lock; +mod syscall; +mod timestamp; +mod unwind; +mod utils; +mod writer_memory; +mod writers; use crate::event::InternalEvent; use crate::utils::read_file; @@ -54,49 +54,22 @@ lazy_static! { let pid = crate::syscall::getpid() as u32; pid }; - pub(crate) static ref CMDLINE: Vec< u8 > = { - read_file( "/proc/self/cmdline" ).unwrap() - }; - pub(crate) static ref EXECUTABLE: Vec< u8 > = { - let executable: Vec< u8 > = read_link( "/proc/self/exe" ).unwrap().as_os_str().as_bytes().into(); + pub(crate) static ref CMDLINE: Vec = { read_file("/proc/self/cmdline").unwrap() }; + pub(crate) static ref EXECUTABLE: Vec = { + let executable: Vec = read_link("/proc/self/exe") + .unwrap() + .as_os_str() + .as_bytes() + .into(); executable }; } pub use crate::api::{ - bytehound_jemalloc_raw_mmap, - bytehound_jemalloc_raw_munmap, - - bytehound_mimalloc_raw_mmap, - bytehound_mimalloc_raw_munmap, - bytehound_mimalloc_raw_mprotect, - - __register_frame, - __deregister_frame, - - _exit, - _Exit, - fork, - - malloc, - calloc, - realloc, - reallocarray, - free, - posix_memalign, - malloc_usable_size, - mmap, - mmap64, - munmap, - mallopt, - memalign, - aligned_alloc, - valloc, - pvalloc, - - memory_profiler_set_marker, - memory_profiler_override_next_timestamp, - memory_profiler_start, - memory_profiler_stop, - memory_profiler_sync + _Exit, __deregister_frame, __register_frame, _exit, aligned_alloc, bytehound_jemalloc_raw_mmap, + bytehound_jemalloc_raw_munmap, bytehound_mimalloc_raw_mmap, bytehound_mimalloc_raw_mprotect, + bytehound_mimalloc_raw_munmap, calloc, fork, free, malloc, malloc_usable_size, mallopt, + memalign, memory_profiler_override_next_timestamp, memory_profiler_set_marker, + memory_profiler_start, memory_profiler_stop, memory_profiler_sync, mmap, mmap64, munmap, + posix_memalign, pvalloc, realloc, reallocarray, valloc, }; diff --git a/preload/src/logger.rs b/preload/src/logger.rs index 5a9e69bd..cec0c3f8 100644 --- a/preload/src/logger.rs +++ b/preload/src/logger.rs @@ -1,47 +1,47 @@ +use libc; +use log::{self, Level, Metadata, Record}; use std::io::{self, Write}; use std::mem; +use std::os::unix::io::{FromRawFd, IntoRawFd}; use std::sync::atomic::{AtomicUsize, Ordering}; -use log::{self, Level, Record, Metadata}; -use std::os::unix::io::{IntoRawFd, FromRawFd}; -use libc; -use crate::utils::{Buffer, stack_format_bytes, temporarily_change_umask}; +use crate::raw_file::{rename, RawFile}; use crate::spin_lock::SpinLock; -use crate::raw_file::{RawFile, rename}; use crate::syscall; +use crate::utils::{stack_format_bytes, temporarily_change_umask, Buffer}; -fn level_to_str( level: Level ) -> &'static str { +fn level_to_str(level: Level) -> &'static str { match level { Level::Error => "ERR", Level::Warn => "WRN", Level::Info => "INF", Level::Debug => "DBG", - Level::Trace => "TRC" + Level::Trace => "TRC", } } pub struct SyscallLogger { level: log::LevelFilter, - pid: libc::pid_t + pid: libc::pid_t, } impl SyscallLogger { pub const fn empty() -> Self { SyscallLogger { level: log::LevelFilter::Off, - pid: 0 + pid: 0, } } - pub fn initialize( &mut self, level: log::LevelFilter, pid: libc::pid_t ) { + pub fn initialize(&mut self, level: log::LevelFilter, pid: libc::pid_t) { self.level = level; self.pid = pid; } } -fn filter( record: &Record ) -> bool { - if let Some( module ) = record.module_path() { - if module.starts_with( "goblin::" ) { +fn filter(record: &Record) -> bool { + if let Some(module) = record.module_path() { + if module.starts_with("goblin::") { return false; } } @@ -49,43 +49,52 @@ fn filter( record: &Record ) -> bool { true } -pub fn raw_eprint( buffer: &[u8] ) { - syscall::write( 2, buffer ); +pub fn raw_eprint(buffer: &[u8]) { + syscall::write(2, buffer); } impl log::Log for SyscallLogger { #[inline] - fn enabled( &self, metadata: &Metadata ) -> bool { + fn enabled(&self, metadata: &Metadata) -> bool { metadata.level() <= self.level } #[inline] - fn log( &self, record: &Record ) { - if self.enabled( record.metadata() ) { - if !filter( record ) { + fn log(&self, record: &Record) { + if self.enabled(record.metadata()) { + if !filter(record) { return; } - stack_format_bytes( format_args!( "bytehound: {:04x} {:04x} {} {}\n", self.pid, syscall::gettid(), level_to_str( record.level() ), record.args() ), |buffer| { - buffer[ buffer.len() - 1 ] = b'\n'; - raw_eprint( buffer ); - }); + stack_format_bytes( + format_args!( + "bytehound: {:04x} {:04x} {} {}\n", + self.pid, + syscall::gettid(), + level_to_str(record.level()), + record.args() + ), + |buffer| { + buffer[buffer.len() - 1] = b'\n'; + raw_eprint(buffer); + }, + ); } } #[inline] - fn flush( &self ) {} + fn flush(&self) {} } struct RotationState { path: Buffer, old_path: Buffer, initial_path: Buffer, - rotated: bool + rotated: bool, } impl RotationState { - fn rotate( &mut self ) -> RawFile { + fn rotate(&mut self) -> RawFile { let path = &self.path; let old_path = if !self.rotated { self.rotated = true; @@ -94,92 +103,92 @@ impl RotationState { &self.old_path }; - if let Err( _ ) = rename( path, old_path ) { - raw_eprint( b"bytehound: Failed to rotate the log file!\n" ); + if let Err(_) = rename(path, old_path) { + raw_eprint(b"bytehound: Failed to rotate the log file!\n"); } let fp = { - let _handle = temporarily_change_umask( 0o777 ); - RawFile::create( &path, 0o777 ).expect( "failed to recreate the logfile after rotation" ) + let _handle = temporarily_change_umask(0o777); + RawFile::create(&path, 0o777).expect("failed to recreate the logfile after rotation") }; - fp.chmod( 0o777 ); + fp.chmod(0o777); fp } } pub struct FileLoggerOutput { raw_fd: AtomicUsize, - rotation: SpinLock< RotationState >, + rotation: SpinLock, bytes_written: AtomicUsize, - rotate_at: Option< usize > + rotate_at: Option, } impl FileLoggerOutput { - fn new( path: Buffer, mut rotate_at: Option< usize > ) -> Result< Self, io::Error > { + fn new(path: Buffer, mut rotate_at: Option) -> Result { let fp = { - let _handle = temporarily_change_umask( 0o777 ); - RawFile::create( &path, 0o777 )? + let _handle = temporarily_change_umask(0o777); + RawFile::create(&path, 0o777)? }; - fp.chmod( 0o777 ); + fp.chmod(0o777); - if rotate_at == Some( 0 ) { + if rotate_at == Some(0) { rotate_at = None; } let mut initial_path = Buffer::new(); - initial_path.write( path.as_slice() ).unwrap(); - initial_path.write( b".initial" ).unwrap(); + initial_path.write(path.as_slice()).unwrap(); + initial_path.write(b".initial").unwrap(); let mut old_path = Buffer::new(); - old_path.write( path.as_slice() ).unwrap(); - old_path.write( b".old" ).unwrap(); + old_path.write(path.as_slice()).unwrap(); + old_path.write(b".old").unwrap(); let output = FileLoggerOutput { - raw_fd: AtomicUsize::new( fp.into_raw_fd() as _ ), - rotation: SpinLock::new( RotationState { + raw_fd: AtomicUsize::new(fp.into_raw_fd() as _), + rotation: SpinLock::new(RotationState { path, old_path, initial_path, - rotated: false + rotated: false, }), - bytes_written: AtomicUsize::new( 0 ), - rotate_at + bytes_written: AtomicUsize::new(0), + rotate_at, }; - Ok( output ) + Ok(output) } - fn fd( &self ) -> libc::c_int { - self.raw_fd.load( Ordering::SeqCst ) as libc::c_int + fn fd(&self) -> libc::c_int { + self.raw_fd.load(Ordering::SeqCst) as libc::c_int } - fn rotate_if_necessary( &self ) -> Result< (), io::Error > { + fn rotate_if_necessary(&self) -> Result<(), io::Error> { let threshold = match self.rotate_at { - Some( threshold ) => threshold, - None => return Ok(()) + Some(threshold) => threshold, + None => return Ok(()), }; - if self.bytes_written.load( Ordering::Relaxed ) < threshold { + if self.bytes_written.load(Ordering::Relaxed) < threshold { return Ok(()); } let mut rotation = match self.rotation.try_lock() { - Some( rotation ) => rotation, - None => return Ok(()) + Some(rotation) => rotation, + None => return Ok(()), }; - if self.bytes_written.load( Ordering::SeqCst ) < threshold { + if self.bytes_written.load(Ordering::SeqCst) < threshold { return Ok(()); } let new_fp = rotation.rotate(); let new_fd = new_fp.into_raw_fd(); - let old_fd = self.raw_fd.swap( new_fd as _, Ordering::SeqCst ) as _; - self.bytes_written.store( 0, Ordering::SeqCst ); + let old_fd = self.raw_fd.swap(new_fd as _, Ordering::SeqCst) as _; + self.bytes_written.store(0, Ordering::SeqCst); - mem::drop( unsafe { RawFile::from_raw_fd( old_fd ) } ); + mem::drop(unsafe { RawFile::from_raw_fd(old_fd) }); Ok(()) } @@ -188,7 +197,7 @@ impl FileLoggerOutput { pub struct FileLogger { level: log::LevelFilter, pid: libc::pid_t, - output: Option< FileLoggerOutput > + output: Option, } impl FileLogger { @@ -196,44 +205,61 @@ impl FileLogger { FileLogger { level: log::LevelFilter::Off, pid: 0, - output: None + output: None, } } - pub fn initialize( &mut self, path: Buffer, rotate_at: Option< usize >, level: log::LevelFilter, pid: libc::pid_t ) -> io::Result< () > { - let output = FileLoggerOutput::new( path, rotate_at )?; + pub fn initialize( + &mut self, + path: Buffer, + rotate_at: Option, + level: log::LevelFilter, + pid: libc::pid_t, + ) -> io::Result<()> { + let output = FileLoggerOutput::new(path, rotate_at)?; self.level = level; self.pid = pid; - self.output = Some( output ); + self.output = Some(output); Ok(()) } } impl log::Log for FileLogger { #[inline] - fn enabled( &self, metadata: &Metadata ) -> bool { + fn enabled(&self, metadata: &Metadata) -> bool { metadata.level() <= self.level } #[inline] - fn log( &self, record: &Record ) { - if self.enabled( record.metadata() ) { - if !filter( record ) { + fn log(&self, record: &Record) { + if self.enabled(record.metadata()) { + if !filter(record) { return; } - if let Some( output ) = self.output.as_ref() { - stack_format_bytes( format_args!( "{:04x} {:04x} {} {}\n", self.pid, syscall::gettid(), level_to_str( record.level() ), record.args() ), |buffer| { - let fd = output.fd(); - let mut fp = RawFile::borrow_raw( &fd ); - let _ = fp.write_all( buffer ); - output.bytes_written.fetch_add( buffer.len(), Ordering::Relaxed ); - }); + if let Some(output) = self.output.as_ref() { + stack_format_bytes( + format_args!( + "{:04x} {:04x} {} {}\n", + self.pid, + syscall::gettid(), + level_to_str(record.level()), + record.args() + ), + |buffer| { + let fd = output.fd(); + let mut fp = RawFile::borrow_raw(&fd); + let _ = fp.write_all(buffer); + output + .bytes_written + .fetch_add(buffer.len(), Ordering::Relaxed); + }, + ); let _ = output.rotate_if_necessary(); } } } #[inline] - fn flush( &self ) {} + fn flush(&self) {} } diff --git a/preload/src/macros.rs b/preload/src/macros.rs index c83084fb..353c8b5d 100644 --- a/preload/src/macros.rs +++ b/preload/src/macros.rs @@ -1,16 +1,16 @@ #[inline(never)] #[cold] -pub fn fatal_error( args: std::fmt::Arguments ) -> ! { +pub fn fatal_error(args: std::fmt::Arguments) -> ! { use log::Log; let record = log::RecordBuilder::new() - .level( log::Level::Error ) - .args( args ) + .level(log::Level::Error) + .args(args) .build(); unsafe { - crate::init::SYSCALL_LOGGER.log( &record ); - crate::init::FILE_LOGGER.log( &record ); + crate::init::SYSCALL_LOGGER.log(&record); + crate::init::FILE_LOGGER.log(&record); } panic!(); @@ -37,14 +37,17 @@ macro_rules! assert_eq { match (&$lhs, &$rhs) { (lhs, rhs) => { if lhs != rhs { - $crate::macros::fatal_error( format_args!( + $crate::macros::fatal_error(format_args!( "assertion failed at {}:{}: {} == {}", - std::file!(), std::line!(), stringify!( $lhs ), stringify!( $rhs ) + std::file!(), + std::line!(), + stringify!($lhs), + stringify!($rhs) )); } } } - }} + }}; } macro_rules! assert_ne { @@ -52,12 +55,15 @@ macro_rules! assert_ne { match (&$lhs, &$rhs) { (lhs, rhs) => { if lhs == rhs { - $crate::macros::fatal_error( format_args!( + $crate::macros::fatal_error(format_args!( "assertion failed at {}:{}: {} != {}", - std::file!(), std::line!(), stringify!( $lhs ), stringify!( $rhs ) + std::file!(), + std::line!(), + stringify!($lhs), + stringify!($rhs) )); } } } - }} + }}; } diff --git a/preload/src/nohash.rs b/preload/src/nohash.rs index ddfe6494..a026d202 100644 --- a/preload/src/nohash.rs +++ b/preload/src/nohash.rs @@ -3,30 +3,30 @@ pub struct NoHash; impl std::hash::BuildHasher for NoHash { type Hasher = NoHasher; - fn build_hasher( &self ) -> Self::Hasher { - NoHasher( 0 ) + fn build_hasher(&self) -> Self::Hasher { + NoHasher(0) } } -pub struct NoHasher( u64 ); +pub struct NoHasher(u64); impl std::hash::Hasher for NoHasher { - fn finish( &self ) -> u64 { + fn finish(&self) -> u64 { self.0 } - fn write( &mut self, _bytes: &[u8] ) { + fn write(&mut self, _bytes: &[u8]) { unimplemented!() } - fn write_u32( &mut self, value: u32 ) { + fn write_u32(&mut self, value: u32) { self.0 ^= value as u64; } - fn write_u64( &mut self, value: u64 ) { + fn write_u64(&mut self, value: u64) { self.0 ^= value; } - fn write_usize( &mut self, value: usize ) { + fn write_usize(&mut self, value: usize) { self.0 ^= value as u64; } } diff --git a/preload/src/opt.rs b/preload/src/opt.rs index bc8107a4..841e9063 100644 --- a/preload/src/opt.rs +++ b/preload/src/opt.rs @@ -4,13 +4,13 @@ pub struct Opts { is_initialized: bool, pub base_server_port: u16, - pub chown_output_to: Option< u32 >, + pub chown_output_to: Option, pub disabled_by_default: bool, pub enable_broadcasts: bool, pub enable_server: bool, pub enable_shadow_stack: bool, pub grab_backtraces_on_free: bool, - pub include_file: Option< Buffer >, + pub include_file: Option, pub output_path_pattern: Buffer, pub register_sigusr1: bool, pub register_sigusr2: bool, @@ -22,7 +22,7 @@ pub struct Opts { pub backtrace_cache_size_level_2: usize, pub cull_temporary_allocations: bool, pub temporary_allocation_lifetime_threshold: u64, - pub temporary_allocation_pending_threshold: Option< usize >, + pub temporary_allocation_pending_threshold: Option, pub track_child_processes: bool, pub disable_pr_set_vma_anon_name: bool, } @@ -38,7 +38,7 @@ static mut OPTS: Opts = Opts { enable_shadow_stack: true, grab_backtraces_on_free: true, include_file: None, - output_path_pattern: Buffer::from_fixed_slice( b"memory-profiling_%e_%t_%p.dat" ), + output_path_pattern: Buffer::from_fixed_slice(b"memory-profiling_%e_%t_%p.dat"), register_sigusr1: true, register_sigusr2: true, use_perf_event_open: true, @@ -55,49 +55,54 @@ static mut OPTS: Opts = Opts { }; trait ParseVar: Sized { - fn parse_var( value: Buffer ) -> Option< Self >; + fn parse_var(value: Buffer) -> Option; } impl ParseVar for bool { - fn parse_var( value: Buffer ) -> Option< Self > { - Some( value.as_slice() == b"1" || value.as_slice() == b"true" ) + fn parse_var(value: Buffer) -> Option { + Some(value.as_slice() == b"1" || value.as_slice() == b"true") } } impl ParseVar for u16 { - fn parse_var( value: Buffer ) -> Option< Self > { + fn parse_var(value: Buffer) -> Option { value.to_str()?.parse().ok() } } impl ParseVar for u32 { - fn parse_var( value: Buffer ) -> Option< Self > { + fn parse_var(value: Buffer) -> Option { value.to_str()?.parse().ok() } } impl ParseVar for u64 { - fn parse_var( value: Buffer ) -> Option< Self > { + fn parse_var(value: Buffer) -> Option { value.to_str()?.parse().ok() } } impl ParseVar for usize { - fn parse_var( value: Buffer ) -> Option< Self > { + fn parse_var(value: Buffer) -> Option { value.to_str()?.parse().ok() } } impl ParseVar for Buffer { - fn parse_var( value: Buffer ) -> Option< Self > { - value.to_str().and_then( |value| Buffer::from_slice( value.as_bytes() ) ) + fn parse_var(value: Buffer) -> Option { + value + .to_str() + .and_then(|value| Buffer::from_slice(value.as_bytes())) } } -impl< T > ParseVar for Option< T > where T: ParseVar { - fn parse_var( value: Buffer ) -> Option< Self > { - if let Some( value ) = T::parse_var( value ) { - Some( Some( value ) ) +impl ParseVar for Option +where + T: ParseVar, +{ + fn parse_var(value: Buffer) -> Option { + if let Some(value) = T::parse_var(value) { + Some(Some(value)) } else { None } @@ -119,7 +124,7 @@ macro_rules! opts { } pub unsafe fn initialize() { - info!( "Options:" ); + info!("Options:"); let opts = &mut OPTS; opts! { @@ -165,7 +170,9 @@ pub fn is_initialized() -> bool { #[inline(never)] #[cold] fn crash() { - crate::logger::raw_eprint( b"bytehound: fatal error: opts accessed before they're initialized\n" ); + crate::logger::raw_eprint( + b"bytehound: fatal error: opts accessed before they're initialized\n", + ); unsafe { libc::abort(); } @@ -193,14 +200,15 @@ pub fn emit_partial_backtraces() -> bool { lazy_static! { static ref VALUE: bool = { - let value = unsafe { crate::syscall::getenv( b"MEMORY_PROFILER_EMIT_PARTIAL_BACKTRACES" ) } - .map( |value| value.as_slice() == b"1" ) - .unwrap_or( true ); + let value = + unsafe { crate::syscall::getenv(b"MEMORY_PROFILER_EMIT_PARTIAL_BACKTRACES") } + .map(|value| value.as_slice() == b"1") + .unwrap_or(true); if value { - info!( "Will emit partial backtraces" ); + info!("Will emit partial backtraces"); } else { - info!( "Will NOT emit partial backtraces" ); + info!("Will NOT emit partial backtraces"); } value diff --git a/preload/src/ordered_map.rs b/preload/src/ordered_map.rs index 5bd3e14e..80c1f17b 100644 --- a/preload/src/ordered_map.rs +++ b/preload/src/ordered_map.rs @@ -1,130 +1,134 @@ -use { - std::{ - collections::{ - hash_map::{ - Entry, - RandomState - }, - HashMap - }, - hash::{ - BuildHasher, - Hash - }, - marker::{ - PhantomData - } - } +use std::{ + collections::{ + hash_map::{Entry, RandomState}, + HashMap, + }, + hash::{BuildHasher, Hash}, + marker::PhantomData, }; #[derive(Clone, PartialEq, Eq, Debug, Hash)] -struct Item< K, V > { - prev: Option< K >, - next: Option< K >, - value: V +struct Item { + prev: Option, + next: Option, + value: V, } #[derive(Clone, Debug)] -pub struct OrderedMap< K, V, S = RandomState > where K: Copy + PartialEq + Eq + Hash { - first_and_last: Option< (K, K) >, - map: HashMap< K, Item< K, V >, S >, - phantom: PhantomData< S > +pub struct OrderedMap +where + K: Copy + PartialEq + Eq + Hash, +{ + first_and_last: Option<(K, K)>, + map: HashMap, S>, + phantom: PhantomData, } -impl< K, V, S > Default for OrderedMap< K, V, S > where K: Copy + PartialEq + Eq + Hash, S: BuildHasher + Default { +impl Default for OrderedMap +where + K: Copy + PartialEq + Eq + Hash, + S: BuildHasher + Default, +{ fn default() -> Self { OrderedMap { first_and_last: None, map: Default::default(), - phantom: PhantomData + phantom: PhantomData, } } } -impl< K, V > OrderedMap< K, V, RandomState > where K: Copy + PartialEq + Eq + Hash { +impl OrderedMap +where + K: Copy + PartialEq + Eq + Hash, +{ #[allow(dead_code)] pub fn new() -> Self { Self::default() } } -impl< K, V, S > OrderedMap< K, V, S > where K: Copy + PartialEq + Eq + Hash, S: BuildHasher { +impl OrderedMap +where + K: Copy + PartialEq + Eq + Hash, + S: BuildHasher, +{ #[allow(dead_code)] - pub fn is_empty( &self ) -> bool { - debug_assert_eq!( self.first_and_last.is_none(), self.map.is_empty() ); + pub fn is_empty(&self) -> bool { + debug_assert_eq!(self.first_and_last.is_none(), self.map.is_empty()); self.map.is_empty() } - pub fn len( &self ) -> usize { + pub fn len(&self) -> usize { self.map.len() } - pub fn get( &self, key: &K ) -> Option< &V > { - self.map.get( key ).map( |item| &item.value ) + pub fn get(&self, key: &K) -> Option<&V> { + self.map.get(key).map(|item| &item.value) } - pub fn get_mut( &mut self, key: &K ) -> Option< &mut V > { - self.map.get_mut( key ).map( |item| &mut item.value ) + pub fn get_mut(&mut self, key: &K) -> Option<&mut V> { + self.map.get_mut(key).map(|item| &mut item.value) } - pub fn front_key( &self ) -> Option< K > { - self.first_and_last.map( |(key, _)| key ) + pub fn front_key(&self) -> Option { + self.first_and_last.map(|(key, _)| key) } #[allow(dead_code)] - pub fn back_key( &self ) -> Option< K > { - self.first_and_last.map( |(_, key)| key ) + pub fn back_key(&self) -> Option { + self.first_and_last.map(|(_, key)| key) } - pub fn pop_front( &mut self ) -> Option< (K, V) > { + pub fn pop_front(&mut self) -> Option<(K, V)> { let key = self.front_key()?; - self.remove( &key ).map( |value| (key, value) ) + self.remove(&key).map(|value| (key, value)) } - pub fn insert( &mut self, key: K, value: V ) -> Option< V > { - match self.map.entry( key ) { - Entry::Occupied( mut bucket ) => { + pub fn insert(&mut self, key: K, value: V) -> Option { + match self.map.entry(key) { + Entry::Occupied(mut bucket) => { let (first, last) = self.first_and_last.unwrap(); let bucket = bucket.get_mut(); - let old_value = std::mem::replace( &mut bucket.value, value ); + let old_value = std::mem::replace(&mut bucket.value, value); if key != last { let old_next = bucket.next.take().unwrap(); let old_prev = bucket.prev.take(); if key == first { - bucket.prev = Some( last ); - self.first_and_last = Some( (old_next, key) ); + bucket.prev = Some(last); + self.first_and_last = Some((old_next, key)); } else { let old_prev = old_prev.unwrap(); - bucket.prev = Some( last ); - self.first_and_last = Some( (first, key) ); - self.map.get_mut( &old_prev ).unwrap().next = Some( old_next ); + bucket.prev = Some(last); + self.first_and_last = Some((first, key)); + self.map.get_mut(&old_prev).unwrap().next = Some(old_next); } - self.map.get_mut( &old_next ).unwrap().prev = old_prev; - self.map.get_mut( &last ).unwrap().next = Some( key ); + self.map.get_mut(&old_next).unwrap().prev = old_prev; + self.map.get_mut(&last).unwrap().next = Some(key); } - Some( old_value ) - }, - Entry::Vacant( bucket ) => { - if let Some( (first, last) ) = self.first_and_last { - self.first_and_last = Some( (first, key) ); - bucket.insert( Item { - prev: Some( last ), + Some(old_value) + } + Entry::Vacant(bucket) => { + if let Some((first, last)) = self.first_and_last { + self.first_and_last = Some((first, key)); + bucket.insert(Item { + prev: Some(last), next: None, - value + value, }); - self.map.get_mut( &last ).unwrap().next = Some( key ); + self.map.get_mut(&last).unwrap().next = Some(key); } else { - self.first_and_last = Some( (key, key) ); - bucket.insert( Item { + self.first_and_last = Some((key, key)); + bucket.insert(Item { prev: None, next: None, - value + value, }); } @@ -134,23 +138,23 @@ impl< K, V, S > OrderedMap< K, V, S > where K: Copy + PartialEq + Eq + Hash, S: } #[allow(dead_code)] - pub fn contains_key( &self, key: &K ) -> bool { - self.map.contains_key( key ) + pub fn contains_key(&self, key: &K) -> bool { + self.map.contains_key(key) } - pub fn remove( &mut self, key: &K ) -> Option< V > { + pub fn remove(&mut self, key: &K) -> Option { let key = *key; - if let Some( item ) = self.map.remove( &key ) { + if let Some(item) = self.map.remove(&key) { if self.map.is_empty() { self.first_and_last = None; - return Some( item.value ); + return Some(item.value); } - if let Some( next ) = item.next { - self.map.get_mut( &next ).unwrap().prev = item.prev; + if let Some(next) = item.next { + self.map.get_mut(&next).unwrap().prev = item.prev; } - if let Some( prev ) = item.prev { - self.map.get_mut( &prev ).unwrap().next = item.next; + if let Some(prev) = item.prev { + self.map.get_mut(&prev).unwrap().next = item.next; } let (mut first, mut last) = self.first_and_last.unwrap(); @@ -160,9 +164,9 @@ impl< K, V, S > OrderedMap< K, V, S > where K: Copy + PartialEq + Eq + Hash, S: if last == key { last = item.prev.unwrap(); } - self.first_and_last = Some( (first, last) ); + self.first_and_last = Some((first, last)); - Some( item.value ) + Some(item.value) } else { None } @@ -173,71 +177,71 @@ impl< K, V, S > OrderedMap< K, V, S > where K: Copy + PartialEq + Eq + Hash, S: fn test_ordered_copy_map() { macro_rules! check { ($map:expr, $expected:expr) => { - let expected: Vec< i32 > = $expected.iter().copied().collect(); + let expected: Vec = $expected.iter().copied().collect(); let mut actual_front = Vec::new(); let mut actual_back = Vec::new(); { let mut map = $map.clone(); - while let Some( key ) = map.front_key() { - map.remove( &key ); - actual_front.push( key ); + while let Some(key) = map.front_key() { + map.remove(&key); + actual_front.push(key); } } { let mut map = $map.clone(); - while let Some( key ) = map.back_key() { - map.remove( &key ); - actual_back.push( key ); + while let Some(key) = map.back_key() { + map.remove(&key); + actual_back.push(key); } } - assert_eq!( actual_front, expected ); - assert_eq!( $map.len(), expected.len() ); + assert_eq!(actual_front, expected); + assert_eq!($map.len(), expected.len()); actual_back.reverse(); - assert_eq!( actual_front, actual_back ); - } + assert_eq!(actual_front, actual_back); + }; } macro_rules! insert { ($map:expr, $key:expr) => { - $map.insert( $key, () ); - } + $map.insert($key, ()); + }; } let mut map = OrderedMap::new(); - check!( map, &[] ); + check!(map, &[]); - insert!( map, 10 ); - check!( map, &[10] ); + insert!(map, 10); + check!(map, &[10]); - insert!( map, 5 ); - check!( map, &[10, 5] ); + insert!(map, 5); + check!(map, &[10, 5]); - insert!( map, 15 ); - check!( map, &[10, 5, 15] ); + insert!(map, 15); + check!(map, &[10, 5, 15]); - insert!( map, 15 ); // Insert one that's already at the end. - check!( map, &[10, 5, 15] ); + insert!(map, 15); // Insert one that's already at the end. + check!(map, &[10, 5, 15]); - insert!( map, 5 ); // Insert one from the middle. - check!( map, &[10, 15, 5] ); + insert!(map, 5); // Insert one from the middle. + check!(map, &[10, 15, 5]); - insert!( map, 10 ); // Insert one from the start. - check!( map, &[15, 5, 10] ); + insert!(map, 10); // Insert one from the start. + check!(map, &[15, 5, 10]); - insert!( map, 20 ); - check!( map, &[15, 5, 10, 20] ); + insert!(map, 20); + check!(map, &[15, 5, 10, 20]); - map.remove( &15 ); // Remove first. - check!( map, &[5, 10, 20] ); + map.remove(&15); // Remove first. + check!(map, &[5, 10, 20]); - map.remove( &10 ); // Remove middle. - check!( map, &[5, 20] ); + map.remove(&10); // Remove middle. + check!(map, &[5, 20]); - map.remove( &20 ); // Remove last. - check!( map, &[5] ); + map.remove(&20); // Remove last. + check!(map, &[5]); - map.remove( &5 ); // Remove first and last. - check!( map, &[] ); + map.remove(&5); // Remove first and last. + check!(map, &[]); } diff --git a/preload/src/processing_thread.rs b/preload/src/processing_thread.rs index 689024ae..e61ffbc1 100644 --- a/preload/src/processing_thread.rs +++ b/preload/src/processing_thread.rs @@ -1,56 +1,43 @@ +use std::fs::{self, remove_file, File}; use std::hash::Hash; use std::mem; -use std::fs::{self, File, remove_file}; +use std::net::{IpAddr, SocketAddr, TcpListener, TcpStream, UdpSocket}; +use std::num::NonZeroUsize; use std::os::unix::fs::{OpenOptionsExt, PermissionsExt}; -use std::net::{TcpListener, TcpStream, UdpSocket, IpAddr, SocketAddr}; -use std::time::Duration; use std::path::{Path, PathBuf}; use std::sync::atomic::AtomicUsize; +use std::time::Duration; use std::os::unix::io::AsRawFd; -use std::io::{ - self, - Write, - Seek, - SeekFrom -}; +use std::io::{self, Seek, SeekFrom, Write}; -use common::speedy::{Writable, Readable}; +use common::speedy::{Readable, Writable}; use common::event::{DataId, Event}; -use common::lz4_stream::Lz4Writer; -use common::request::{ - PROTOCOL_VERSION, - Request, - Response, - BroadcastHeader -}; use common::get_local_ips; +use common::lz4_stream::Lz4Writer; +use common::request::{BroadcastHeader, Request, Response, PROTOCOL_VERSION}; -use crate::{CMDLINE, EXECUTABLE, PID}; +use crate::allocation_tracker::{AllocationBucket, BufferedAllocation}; use crate::arch; -use crate::event::{InternalEvent, send_event, timed_recv_all_events}; +use crate::event::{send_event, timed_recv_all_events, InternalEvent}; use crate::global::AllocationLock; +use crate::nohash::NoHash; use crate::opt; -use crate::timestamp::{Timestamp, get_timestamp, get_wall_clock}; -use crate::utils::{ - generate_filename, - copy, - temporarily_change_umask -}; +use crate::smaps::update_smaps; +use crate::timestamp::{get_timestamp, get_wall_clock, Timestamp}; +use crate::unwind::Backtrace; +use crate::utils::{copy, generate_filename, temporarily_change_umask}; use crate::writer_memory; use crate::writers; -use crate::nohash::NoHash; -use crate::unwind::Backtrace; -use crate::allocation_tracker::{AllocationBucket, BufferedAllocation}; -use crate::smaps::update_smaps; +use crate::{CMDLINE, EXECUTABLE, PID}; -fn get_hash< T: Hash >( value: T ) -> u64 { +fn get_hash(value: T) -> u64 { use std::collections::hash_map::DefaultHasher; use std::hash::Hasher; let mut hasher = DefaultHasher::new(); - value.hash( &mut hasher ); + value.hash(&mut hasher); hasher.finish() } @@ -61,57 +48,62 @@ fn generate_data_id() -> DataId { let mut timespec = libc::timespec { tv_sec: 0, - tv_nsec: 0 + tv_nsec: 0, }; unsafe { - libc::clock_gettime( libc::CLOCK_REALTIME, &mut timespec ); + libc::clock_gettime(libc::CLOCK_REALTIME, &mut timespec); } - let a = get_hash( &pid as *const _ as usize ) ^ get_hash( pid ) ^ get_hash( timespec.tv_sec ); - let b = get_hash( cmdline ) ^ get_hash( executable ) ^ get_hash( timespec.tv_nsec ); + let a = get_hash(&pid as *const _ as usize) ^ get_hash(pid) ^ get_hash(timespec.tv_sec); + let b = get_hash(cmdline) ^ get_hash(executable) ^ get_hash(timespec.tv_nsec); - DataId::new( a, b ) + DataId::new(a, b) } struct Output { - file: Option< (PathBuf, File) >, - clients: Vec< Client > + file: Option<(PathBuf, File)>, + clients: Vec, } impl Output { fn new() -> Self { Output { file: None, - clients: Vec::new() + clients: Vec::new(), } } - fn set_file( &mut self, fp: File, path: PathBuf ) { - self.file = Some( (path, fp) ); + fn set_file(&mut self, fp: File, path: PathBuf) { + self.file = Some((path, fp)); } - fn is_none( &self ) -> bool { + fn is_none(&self) -> bool { self.file.is_none() && self.clients.is_empty() } } -fn poll_clients( id: DataId, initial_timestamp: Timestamp, poll_fds: &mut Vec< libc::pollfd >, output: &mut Lz4Writer< Output > ) { +fn poll_clients( + id: DataId, + initial_timestamp: Timestamp, + poll_fds: &mut Vec, + output: &mut Lz4Writer, +) { poll_fds.clear(); for client in output.inner().clients.iter() { - poll_fds.push( libc::pollfd { + poll_fds.push(libc::pollfd { fd: client.stream.as_raw_fd(), events: libc::POLLIN | libc::POLLHUP, - revents: 0 + revents: 0, }); } - let ok = unsafe { libc::poll( poll_fds.as_ptr() as *mut _, poll_fds.len() as _, 0 ) }; + let ok = unsafe { libc::poll(poll_fds.as_ptr() as *mut _, poll_fds.len() as _, 0) }; if ok == -1 { let err = io::Error::last_os_error(); if err.kind() != io::ErrorKind::Interrupted { - error!( "Poll failed: {}", err ); + error!("Poll failed: {}", err); return; } } @@ -120,9 +112,9 @@ fn poll_clients( id: DataId, initial_timestamp: Timestamp, poll_fds: &mut Vec< l let pollin = poll_fd.revents & libc::POLLIN != 0; let pollhup = poll_fd.revents & libc::POLLHUP != 0; - let client = &mut output.inner_mut_without_flush().clients[ index ]; + let client = &mut output.inner_mut_without_flush().clients[index]; if pollhup { - info!( "A client was disconnected" ); + info!("A client was disconnected"); client.running = false; continue; } @@ -131,50 +123,54 @@ fn poll_clients( id: DataId, initial_timestamp: Timestamp, poll_fds: &mut Vec< l continue; } - trace!( "Reading a client's request..." ); - let request = match Request::read_from_stream_unbuffered( &mut client.stream ) { - Ok( request ) => request, - Err( error ) => { - info!( "Failed to read a client request: {}", error ); + trace!("Reading a client's request..."); + let request = match Request::read_from_stream_unbuffered(&mut client.stream) { + Ok(request) => request, + Err(error) => { + info!("Failed to read a client request: {}", error); client.running = false; continue; } }; - trace!( "Finished reading the request from client" ); + trace!("Finished reading the request from client"); match request { Request::StartStreaming => { let output = &mut output.inner_mut().unwrap(); - let client = &mut output.clients[ index ]; - if let Err( error ) = client.start_streaming( id, initial_timestamp, &mut output.file ) { - info!( "Failed to start streaming to a client: {}", error ); + let client = &mut output.clients[index]; + if let Err(error) = client.start_streaming(id, initial_timestamp, &mut output.file) + { + info!("Failed to start streaming to a client: {}", error); client.running = false; } else { client.streaming = true; } - }, + } Request::TriggerMemoryDump => { - debug!( "Received a TriggerMemoryDump request" ); - send_event( InternalEvent::GrabMemoryDump ); - }, + debug!("Received a TriggerMemoryDump request"); + send_event(InternalEvent::GrabMemoryDump); + } Request::Ping => { - trace!( "Received a Ping request" ); - if let Err( error ) = Response::Pong.write_to_stream( &mut client.stream ) { - info!( "Failed to respond to a client ping: {}", error ); + trace!("Received a Ping request"); + if let Err(error) = Response::Pong.write_to_stream(&mut client.stream) { + info!("Failed to respond to a client ping: {}", error); client.running = false; } } } } - output.inner_mut_without_flush().clients.retain( |client| client.running ); + output + .inner_mut_without_flush() + .clients + .retain(|client| client.running); } impl io::Write for Output { - fn write( &mut self, data: &[u8] ) -> io::Result< usize > { - if let Some( (ref path, ref mut fp) ) = self.file { - if let Err( error ) = fp.write_all( data ) { - warn!( "Write to {:?} failed: {}", path, error ); + fn write(&mut self, data: &[u8]) -> io::Result { + if let Some((ref path, ref mut fp)) = self.file { + if let Err(error) = fp.write_all(data) { + warn!("Write to {:?} failed: {}", path, error); self.file = None; } } @@ -184,20 +180,20 @@ impl io::Write for Output { continue; } - let result = client.write_all( data ); - if let Err( error ) = result { + let result = client.write_all(data); + if let Err(error) = result { client.running = false; - warn!( "Write to client failed: {}", error ); + warn!("Write to client failed: {}", error); } } - Ok( data.len() ) + Ok(data.len()) } - fn flush( &mut self ) -> io::Result< () > { - if let Some( (ref path, ref mut fp) ) = self.file { - if let Err( error ) = fp.flush() { - warn!( "Flush of {:?} failed: {}", path, error ); + fn flush(&mut self) -> io::Result<()> { + if let Some((ref path, ref mut fp)) = self.file { + if let Err(error) = fp.flush() { + warn!("Flush of {:?} failed: {}", path, error); self.file = None; } } @@ -206,15 +202,15 @@ impl io::Write for Output { } } -impl< 'a > io::Write for &'a mut Client { - fn write( &mut self, data: &[u8] ) -> io::Result< usize > { +impl<'a> io::Write for &'a mut Client { + fn write(&mut self, data: &[u8]) -> io::Result { let length = data.len(); - let response = Response::Data( data.into() ); - let count = response.write_to_stream( &mut self.stream ).map( |_| length )?; - Ok( count ) + let response = Response::Data(data.into()); + let count = response.write_to_stream(&mut self.stream).map(|_| length)?; + Ok(count) } - fn flush( &mut self ) -> io::Result< () > { + fn flush(&mut self) -> io::Result<()> { self.stream.flush() } } @@ -222,81 +218,101 @@ impl< 'a > io::Write for &'a mut Client { struct Client { stream: TcpStream, running: bool, - streaming: bool + streaming: bool, } impl Client { - fn new( id: DataId, initial_timestamp: Timestamp, listener_port: u16, stream: TcpStream ) -> io::Result< Self > { + fn new( + id: DataId, + initial_timestamp: Timestamp, + listener_port: u16, + stream: TcpStream, + ) -> io::Result { let mut client = Client { stream, running: true, - streaming: false + streaming: false, }; - Response::Start( broadcast_header( id, initial_timestamp, listener_port ) ).write_to_stream( &mut client.stream )?; - Ok( client ) + Response::Start(broadcast_header(id, initial_timestamp, listener_port)) + .write_to_stream(&mut client.stream)?; + Ok(client) } - fn stream_initial_data( &mut self, id: DataId, initial_timestamp: Timestamp, path: &Path, file: &mut File ) -> io::Result< () > { + fn stream_initial_data( + &mut self, + id: DataId, + initial_timestamp: Timestamp, + path: &Path, + file: &mut File, + ) -> io::Result<()> { if !opt::get().write_binaries_to_output { - info!( "Streaming the binaries which were suppressed in the original output file..." ); - let mut serializer = Lz4Writer::new( &mut *self ); - writers::write_header( id, initial_timestamp, &mut serializer )?; - writers::write_binaries( &mut serializer )?; + info!("Streaming the binaries which were suppressed in the original output file..."); + let mut serializer = Lz4Writer::new(&mut *self); + writers::write_header(id, initial_timestamp, &mut serializer)?; + writers::write_binaries(&mut serializer)?; serializer.flush()?; } - info!( "Streaming initial data..." ); - file.seek( SeekFrom::Start( 0 ) )?; - copy( file, &mut *self )?; + info!("Streaming initial data..."); + file.seek(SeekFrom::Start(0))?; + copy(file, &mut *self)?; - Response::FinishedInitialStreaming.write_to_stream( &mut self.stream )?; + Response::FinishedInitialStreaming.write_to_stream(&mut self.stream)?; - if let Err( error ) = remove_file( &path ) { - warn!( "Failed to remove {:?}: {}", path, error ); + if let Err(error) = remove_file(&path) { + warn!("Failed to remove {:?}: {}", path, error); } - info!( "Finished streaming initial data" ); + info!("Finished streaming initial data"); Ok(()) } - fn start_streaming( &mut self, id: DataId, initial_timestamp: Timestamp, output: &mut Option< (PathBuf, File) > ) -> io::Result< () > { + fn start_streaming( + &mut self, + id: DataId, + initial_timestamp: Timestamp, + output: &mut Option<(PathBuf, File)>, + ) -> io::Result<()> { // First client which connects to us gets streamed all of the data // which we've gathered up until this point. - if let Some( (path, mut fp) ) = output.take() { - match self.stream_initial_data( id, initial_timestamp, &path, &mut fp ) { + if let Some((path, mut fp)) = output.take() { + match self.stream_initial_data(id, initial_timestamp, &path, &mut fp) { Ok(()) => return Ok(()), - Err( error ) => { - fp.seek( SeekFrom::End( 0 ) )?; - *output = Some( (path, fp) ); - return Err( error ); + Err(error) => { + fp.seek(SeekFrom::End(0))?; + *output = Some((path, fp)); + return Err(error); } } } { - let mut serializer = Lz4Writer::new( &mut *self ); - writers::write_header( id, initial_timestamp, &mut serializer )?; - writers::write_maps( &mut serializer )?; - writers::write_binaries( &mut serializer )?; + let mut serializer = Lz4Writer::new(&mut *self); + writers::write_header(id, initial_timestamp, &mut serializer)?; + writers::write_maps(&mut serializer)?; + writers::write_binaries(&mut serializer)?; serializer.flush()?; } - Response::FinishedInitialStreaming - .write_to_stream( &mut self.stream )?; + Response::FinishedInitialStreaming.write_to_stream(&mut self.stream)?; Ok(()) } } impl Drop for Client { - fn drop( &mut self ) { - info!( "Removing client..." ); + fn drop(&mut self) { + info!("Removing client..."); } } -fn broadcast_header( id: DataId, initial_timestamp: Timestamp, listener_port: u16 ) -> BroadcastHeader { +fn broadcast_header( + id: DataId, + initial_timestamp: Timestamp, + listener_port: u16, +) -> BroadcastHeader { let (timestamp, wall_clock_secs, wall_clock_nsecs) = get_wall_clock(); BroadcastHeader { @@ -310,57 +326,68 @@ fn broadcast_header( id: DataId, initial_timestamp: Timestamp, listener_port: u1 cmdline: CMDLINE.clone(), executable: EXECUTABLE.clone(), arch: arch::TARGET_ARCH.to_string(), - protocol_version: PROTOCOL_VERSION + protocol_version: PROTOCOL_VERSION, } } -fn create_listener() -> Option< TcpListener > { +fn create_listener() -> Option { let base_port = opt::get().base_server_port; let mut port = base_port; let listener = loop { - match TcpListener::bind( format!( "0.0.0.0:{}", port ) ) { - Ok( listener ) => { - info!( "Created a TCP listener on port {}", port ); + match TcpListener::bind(format!("0.0.0.0:{}", port)) { + Ok(listener) => { + info!("Created a TCP listener on port {}", port); break listener; - }, - Err( error ) => { + } + Err(error) => { port += 1; if port > base_port + 100 { - error!( "Failed to create a TCP listener: {}", error ); + error!("Failed to create a TCP listener: {}", error); return None; } } } }; - if let Err( error ) = listener.set_nonblocking( true ) { - error!( "Failed to set the TCP listener as non-blocking: {}", error ); + if let Err(error) = listener.set_nonblocking(true) { + error!("Failed to set the TCP listener as non-blocking: {}", error); return None; } - Some( listener ) + Some(listener) } -fn send_broadcast_to( id: DataId, initial_timestamp: Timestamp, listener_port: u16, target: IpAddr ) -> Result< (), io::Error > { - let socket = UdpSocket::bind( SocketAddr::new( target, 0 ) )?; - socket.set_broadcast( true )?; +fn send_broadcast_to( + id: DataId, + initial_timestamp: Timestamp, + listener_port: u16, + target: IpAddr, +) -> Result<(), io::Error> { + let socket = UdpSocket::bind(SocketAddr::new(target, 0))?; + socket.set_broadcast(true)?; let mut message = Vec::new(); - broadcast_header( id, initial_timestamp, listener_port ).write_to_stream( &mut message ).unwrap(); + broadcast_header(id, initial_timestamp, listener_port) + .write_to_stream(&mut message) + .unwrap(); - socket.send_to( &message, "255.255.255.255:43512" )?; + socket.send_to(&message, "255.255.255.255:43512")?; Ok(()) } -fn send_broadcast( id: DataId, initial_timestamp: Timestamp, listener_port: u16 ) -> Result< (), io::Error > { +fn send_broadcast( + id: DataId, + initial_timestamp: Timestamp, + listener_port: u16, +) -> Result<(), io::Error> { use std::iter::once; use std::net::Ipv4Addr; - let wildcard: IpAddr = Ipv4Addr::new( 0, 0, 0, 0 ).into(); + let wildcard: IpAddr = Ipv4Addr::new(0, 0, 0, 0).into(); let mut output = Ok(()); - for ip in get_local_ips().into_iter().chain( once( wildcard ) ) { - let result = send_broadcast_to( id, initial_timestamp, listener_port, ip ); + for ip in get_local_ips().into_iter().chain(once(wildcard)) { + let result = send_broadcast_to(id, initial_timestamp, listener_port, ip); if result.is_err() { output = result; } @@ -369,96 +396,103 @@ fn send_broadcast( id: DataId, initial_timestamp: Timestamp, listener_port: u16 output } -fn initialize_output_file() -> Option< (File, PathBuf) > { - static COUNTER: AtomicUsize = AtomicUsize::new( 0 ); +fn initialize_output_file() -> Option<(File, PathBuf)> { + static COUNTER: AtomicUsize = AtomicUsize::new(0); - let output_path = generate_filename( opt::get().output_path_pattern.as_slice(), Some( &COUNTER ) ); + let output_path = generate_filename(opt::get().output_path_pattern.as_slice(), Some(&COUNTER)); let output_path = output_path.to_str().unwrap(); if output_path == "" { return None; } let fp = { - let _handle = temporarily_change_umask( 0o777 ); + let _handle = temporarily_change_umask(0o777); fs::OpenOptions::new() - .read( true ) - .write( true ) - .create( true ) - .truncate( true ) - .mode( 0o777 ) - .open( &output_path ) + .read(true) + .write(true) + .create(true) + .truncate(true) + .mode(0o777) + .open(&output_path) }; let fp = match fp { - Ok( fp ) => fp, - Err( error ) => { - error!( "Couldn't open '{}' for writing: {}", output_path, error ); + Ok(fp) => fp, + Err(error) => { + error!("Couldn't open '{}' for writing: {}", output_path, error); return None; } }; // In the unlikely case of a race condition when setting the umask. - let _ = fp.set_permissions( fs::Permissions::from_mode( 0o777 ) ); + let _ = fp.set_permissions(fs::Permissions::from_mode(0o777)); - info!( "File '{}' opened for writing", output_path ); - if let Some( uid ) = opt::get().chown_output_to { + info!("File '{}' opened for writing", output_path); + if let Some(uid) = opt::get().chown_output_to { let gid = unsafe { libc::getgid() }; - let errcode = unsafe { libc::fchown( fp.as_raw_fd(), uid, gid ) }; + let errcode = unsafe { libc::fchown(fp.as_raw_fd(), uid, gid) }; if errcode != 0 { let err = io::Error::last_os_error(); - warn!( "Couldn't chown '{}' to {}: {}", output_path, uid, err ); + warn!("Couldn't chown '{}' to {}: {}", output_path, uid, err); } else { - info!( "File '{}' was chown'd to {}", output_path, uid ); + info!("File '{}' was chown'd to {}", output_path, uid); } } - Some( (fp, output_path.into()) ) + Some((fp, output_path.into())) } pub struct BacktraceCache { next_id: u64, - cache: lru::LruCache< u64, Backtrace, NoHash > + cache: lru::LruCache, } impl BacktraceCache { - pub fn new( cache_size: usize ) -> Self { + pub fn new(cache_size: usize) -> Self { BacktraceCache { next_id: 1, - cache: lru::LruCache::with_hasher( cache_size, NoHash ) + cache: lru::LruCache::with_hasher(NonZeroUsize::new(cache_size).unwrap(), NoHash), } } - pub(crate) fn assign_id( &mut self, backtrace: &Backtrace ) -> (u64, bool) { + pub(crate) fn assign_id(&mut self, backtrace: &Backtrace) -> (u64, bool) { let key = backtrace.key(); - if let Some( id ) = backtrace.id() { - self.cache.get( &key ); + if let Some(id) = backtrace.id() { + self.cache.get(&key); return (id, false); } - match self.cache.get_mut( &key ) { + match self.cache.get_mut(&key) { None => { - if cfg!( debug_assertions ) { - if self.cache.len() >= self.cache.cap() { - debug!( "2nd level backtrace cache overflow" ); + if cfg!(debug_assertions) { + if self.cache.len() >= self.cache.cap().get() { + debug!("2nd level backtrace cache overflow"); } } let id = self.next_id; self.next_id += 1; - backtrace.set_id( id ); + backtrace.set_id(id); - self.cache.put( key, backtrace.clone() ); + self.cache.put(key, backtrace.clone()); (id, true) - }, - Some( cached_backtrace ) => { - if Backtrace::ptr_eq( &cached_backtrace, &backtrace ) || cached_backtrace.frames() == backtrace.frames() { - (cached_backtrace.id().expect( "internal error: id was not set on a cached backtrace" ), false) + } + Some(cached_backtrace) => { + if Backtrace::ptr_eq(&cached_backtrace, &backtrace) + || cached_backtrace.frames() == backtrace.frames() + { + ( + cached_backtrace + .id() + .expect("internal error: id was not set on a cached backtrace"), + false, + ) } else { - info!( "2nd level backtrace cache conflict detected!" ); + info!("2nd level backtrace cache conflict detected!"); let id = self.next_id; self.next_id += 1; - backtrace.set_id( id ); + backtrace.set_id(id); *cached_backtrace = backtrace.clone(); @@ -469,16 +503,24 @@ impl BacktraceCache { } } -fn emit_allocation_bucket( mut bucket: AllocationBucket, backtrace_cache: &mut BacktraceCache, fp: &mut impl Write ) -> Result< (), std::io::Error > { +fn emit_allocation_bucket( + mut bucket: AllocationBucket, + backtrace_cache: &mut BacktraceCache, + fp: &mut impl Write, +) -> Result<(), std::io::Error> { if bucket.events.len() == 0 { return Ok(()); } - let mut iter = bucket.events.drain( .. ); + let mut iter = bucket.events.drain(..); - let BufferedAllocation { timestamp, allocation, backtrace } = iter.next().unwrap(); + let BufferedAllocation { + timestamp, + allocation, + backtrace, + } = iter.next().unwrap(); let mut old_pointer = allocation.address; - let backtrace = writers::write_backtrace( &mut *fp, backtrace, backtrace_cache )?; + let backtrace = writers::write_backtrace(&mut *fp, backtrace, backtrace_cache)?; Event::AllocEx { id: bucket.id, timestamp, @@ -489,12 +531,18 @@ fn emit_allocation_bucket( mut bucket: AllocationBucket, backtrace_cache: &mut B thread: allocation.tid, flags: allocation.flags, extra_usable_space: 0, - preceding_free_space: 0 - } - }.write_to_stream( &mut *fp )?; + preceding_free_space: 0, + }, + } + .write_to_stream(&mut *fp)?; - while let Some( BufferedAllocation { timestamp, allocation, backtrace } ) = iter.next() { - let backtrace = writers::write_backtrace( &mut *fp, backtrace, backtrace_cache )?; + while let Some(BufferedAllocation { + timestamp, + allocation, + backtrace, + }) = iter.next() + { + let backtrace = writers::write_backtrace(&mut *fp, backtrace, backtrace_cache)?; Event::ReallocEx { id: bucket.id, @@ -507,9 +555,10 @@ fn emit_allocation_bucket( mut bucket: AllocationBucket, backtrace_cache: &mut B thread: allocation.tid, flags: allocation.flags, extra_usable_space: 0, - preceding_free_space: 0 - } - }.write_to_stream( &mut *fp )?; + preceding_free_space: 0, + }, + } + .write_to_stream(&mut *fp)?; old_pointer = allocation.address; } @@ -517,25 +566,25 @@ fn emit_allocation_bucket( mut bucket: AllocationBucket, backtrace_cache: &mut B } pub(crate) fn thread_main() { - info!( "Starting event thread..." ); + info!("Starting event thread..."); let uuid = generate_data_id(); let initial_timestamp = unsafe { crate::global::INITIAL_TIMESTAMP }; - info!( "Data ID: {}", uuid ); + info!("Data ID: {}", uuid); - let mut output_writer = Lz4Writer::new( Output::new() ); - if let Some( (fp, path) ) = initialize_output_file() { - let mut fp = Lz4Writer::new( fp ); - match writers::write_initial_data( uuid, initial_timestamp, &mut fp ) { + let mut output_writer = Lz4Writer::new(Output::new()); + if let Some((fp, path)) = initialize_output_file() { + let mut fp = Lz4Writer::new(fp); + match writers::write_initial_data(uuid, initial_timestamp, &mut fp) { Ok(()) => { let fp = fp.into_inner().unwrap(); let mut output = Output::new(); - output.set_file( fp, path ); - output_writer.replace_inner( output ).unwrap(); - }, - Err( error ) => { - warn!( "Failed to write initial data: {}", error ); + output.set_file(fp, path); + output_writer.replace_inner(output).unwrap(); + } + Err(error) => { + warn!("Failed to write initial data: {}", error); } } } @@ -543,9 +592,12 @@ pub(crate) fn thread_main() { let mut listener = None; if opt::get().enable_server { - if let Some( listener_instance ) = create_listener() { - let listener_port = listener_instance.local_addr().expect( "couldn't grab the local address of the listener" ).port(); - listener = Some( (listener_instance, listener_port) ); + if let Some(listener_instance) = create_listener() { + let listener_port = listener_instance + .local_addr() + .expect("couldn't grab the local address of the listener") + .port(); + listener = Some((listener_instance, listener_port)); } } @@ -560,44 +612,44 @@ pub(crate) fn thread_main() { let mut force_smaps_update = true; let mut timestamp_override = None; let mut poll_fds = Vec::new(); - let mut backtrace_cache = BacktraceCache::new( opt::get().backtrace_cache_size_level_2 ); + let mut backtrace_cache = BacktraceCache::new(opt::get().backtrace_cache_size_level_2); let mut thread_gc = crate::global::ThreadGarbageCollector::default(); let mut smaps_state = crate::smaps::State::new_preallocated(); loop { - timed_recv_all_events( &mut events, Duration::from_millis( 250 ) ); + timed_recv_all_events(&mut events, Duration::from_millis(250)); crate::global::try_disable_if_requested(); coarse_timestamp = get_timestamp(); - if let Some( (ref mut listener, listener_port) ) = listener { + if let Some((ref mut listener, listener_port)) = listener { if (coarse_timestamp - last_broadcast).as_secs() >= 1 { last_broadcast = coarse_timestamp; if opt::get().enable_broadcasts { - let _ = send_broadcast( uuid, initial_timestamp, listener_port ); + let _ = send_broadcast(uuid, initial_timestamp, listener_port); } } if (coarse_timestamp - last_server_poll).as_msecs() >= 250 { last_server_poll = coarse_timestamp; match listener.accept() { - Ok( (stream, _) ) => { - match Client::new( uuid, initial_timestamp, listener_port, stream ) { - Ok( client ) => { - output_writer.inner_mut_without_flush().clients.push( client ); - }, - Err( error ) => { - info!( "Failed to initialize client: {}", error ); + Ok((stream, _)) => { + match Client::new(uuid, initial_timestamp, listener_port, stream) { + Ok(client) => { + output_writer.inner_mut_without_flush().clients.push(client); + } + Err(error) => { + info!("Failed to initialize client: {}", error); } } - }, - Err( ref error ) if error.kind() == io::ErrorKind::WouldBlock => {}, - Err( _ ) => {} + } + Err(ref error) if error.kind() == io::ErrorKind::WouldBlock => {} + Err(_) => {} } - poll_clients( uuid, initial_timestamp, &mut poll_fds, &mut output_writer ); + poll_clients(uuid, initial_timestamp, &mut poll_fds, &mut output_writer); } } - thread_gc.run( coarse_timestamp, &mut events ); + thread_gc.run(coarse_timestamp, &mut events); crate::allocation_tracker::on_tick(); if events.is_empty() && !running { @@ -605,9 +657,9 @@ pub(crate) fn thread_main() { } if events.is_empty() { - if let Some( _lock ) = allocation_lock_for_memory_dump.take() { + if let Some(_lock) = allocation_lock_for_memory_dump.take() { if !output_writer.inner().is_none() { - let _ = writer_memory::write_memory_dump( &mut output_writer ); + let _ = writer_memory::write_memory_dump(&mut output_writer); } } } @@ -620,7 +672,7 @@ pub(crate) fn thread_main() { id, mut timestamp, allocation, - backtrace + backtrace, } => { if skip { continue; @@ -630,9 +682,11 @@ pub(crate) fn thread_main() { timestamp = coarse_timestamp; } - timestamp = timestamp_override.take().unwrap_or( timestamp ); + timestamp = timestamp_override.take().unwrap_or(timestamp); - if let Ok( backtrace ) = writers::write_backtrace( &mut *serializer, backtrace, &mut backtrace_cache ) { + if let Ok(backtrace) = + writers::write_backtrace(&mut *serializer, backtrace, &mut backtrace_cache) + { let _ = Event::AllocEx { id, timestamp, @@ -643,11 +697,12 @@ pub(crate) fn thread_main() { thread: allocation.tid, flags: allocation.flags, extra_usable_space: allocation.extra_usable_space, - preceding_free_space: 0 - } - }.write_to_stream( &mut *serializer ); + preceding_free_space: 0, + }, + } + .write_to_stream(&mut *serializer); } - }, + } InternalEvent::Realloc { id, mut timestamp, @@ -663,9 +718,11 @@ pub(crate) fn thread_main() { timestamp = coarse_timestamp; } - timestamp = timestamp_override.take().unwrap_or( timestamp ); + timestamp = timestamp_override.take().unwrap_or(timestamp); - if let Ok( backtrace ) = writers::write_backtrace( &mut *serializer, backtrace, &mut backtrace_cache ) { + if let Ok(backtrace) = + writers::write_backtrace(&mut *serializer, backtrace, &mut backtrace_cache) + { let event = Event::ReallocEx { id, timestamp, @@ -677,18 +734,18 @@ pub(crate) fn thread_main() { thread: allocation.tid, flags: allocation.flags, extra_usable_space: allocation.extra_usable_space, - preceding_free_space: 0 - } + preceding_free_space: 0, + }, }; - let _ = event.write_to_stream( &mut *serializer ); + let _ = event.write_to_stream(&mut *serializer); } - }, + } InternalEvent::Free { id, address, backtrace, mut timestamp, - tid + tid, } => { if skip { continue; @@ -698,35 +755,43 @@ pub(crate) fn thread_main() { timestamp = coarse_timestamp; } - timestamp = timestamp_override.take().unwrap_or( timestamp ); + timestamp = timestamp_override.take().unwrap_or(timestamp); - let backtrace = - if let Some( backtrace ) = backtrace { - writers::write_backtrace( &mut *serializer, backtrace, &mut backtrace_cache ).ok() - } else { - Some( 0 ) - }; + let backtrace = if let Some(backtrace) = backtrace { + writers::write_backtrace(&mut *serializer, backtrace, &mut backtrace_cache) + .ok() + } else { + Some(0) + }; - if let Some( backtrace ) = backtrace { + if let Some(backtrace) = backtrace { let _ = Event::FreeEx { id: id.into(), timestamp, pointer: address.get() as u64, backtrace, - thread: tid - }.write_to_stream( &mut *serializer ); + thread: tid, + } + .write_to_stream(&mut *serializer); } - }, - InternalEvent::AllocationBucket( bucket ) => { + } + InternalEvent::AllocationBucket(bucket) => { if skip { continue; } - let _ = emit_allocation_bucket( bucket, &mut backtrace_cache, &mut *serializer ); - }, - InternalEvent::Mallopt { param, value, result, mut timestamp, backtrace, thread } => { + let _ = emit_allocation_bucket(bucket, &mut backtrace_cache, &mut *serializer); + } + InternalEvent::Mallopt { + param, + value, + result, + mut timestamp, + backtrace, + thread, + } => { let system_tid = thread.system_tid(); - mem::drop( thread ); + mem::drop(thread); if skip { continue; @@ -736,65 +801,80 @@ pub(crate) fn thread_main() { timestamp = coarse_timestamp; } - let timestamp = timestamp_override.take().unwrap_or( timestamp ); + let timestamp = timestamp_override.take().unwrap_or(timestamp); - if let Ok( backtrace ) = writers::write_backtrace( &mut *serializer, backtrace, &mut backtrace_cache ) { - let event = Event::Mallopt { timestamp, param, value, result, backtrace, thread: system_tid }; - let _ = event.write_to_stream( &mut *serializer ); + if let Ok(backtrace) = + writers::write_backtrace(&mut *serializer, backtrace, &mut backtrace_cache) + { + let event = Event::Mallopt { + timestamp, + param, + value, + result, + backtrace, + thread: system_tid, + }; + let _ = event.write_to_stream(&mut *serializer); } - }, + } InternalEvent::Exit => { crate::allocation_tracker::on_exit(); running = false; force_smaps_update = true; - }, + } InternalEvent::GrabMemoryDump => { // Block any further allocations. if allocation_lock_for_memory_dump.is_none() { - debug!( "Locking allocations to prepare for a memory dump" ); - allocation_lock_for_memory_dump = Some( AllocationLock::new() ); + debug!("Locking allocations to prepare for a memory dump"); + allocation_lock_for_memory_dump = Some(AllocationLock::new()); } - }, + } InternalEvent::SetMarker { value } => { if skip { continue; } let event = Event::Marker { value }; - let _ = event.write_to_stream( &mut *serializer ); - }, + let _ = event.write_to_stream(&mut *serializer); + } InternalEvent::OverrideNextTimestamp { timestamp } => { - timestamp_override = Some( timestamp ); - }, - InternalEvent::AddressSpaceUpdated { timestamp, maps, new_binaries } => { - if opt::get().write_binaries_to_output || serializer.inner_mut_without_flush().file.is_none() { + timestamp_override = Some(timestamp); + } + InternalEvent::AddressSpaceUpdated { + timestamp, + maps, + new_binaries, + } => { + if opt::get().write_binaries_to_output + || serializer.inner_mut_without_flush().file.is_none() + { for binary in new_binaries { - debug!( "Writing new binary: {}", binary.name() ); + debug!("Writing new binary: {}", binary.name()); let event = Event::File64 { timestamp, path: binary.name().into(), - contents: binary.as_bytes().into() + contents: binary.as_bytes().into(), }; - let _ = event.write_to_stream( &mut *serializer ); + let _ = event.write_to_stream(&mut *serializer); } } - debug!( "Writing new maps..." ); + debug!("Writing new maps..."); let event = Event::File64 { timestamp, path: "/proc/self/maps".into(), - contents: maps.as_bytes().into() + contents: maps.as_bytes().into(), }; - let _ = event.write_to_stream( &mut *serializer ); + let _ = event.write_to_stream(&mut *serializer); if opt::get().gather_maps { update_smaps( &mut smaps_state, &mut backtrace_cache, &mut *serializer, - true + true, ); last_smaps_update = timestamp; @@ -805,14 +885,15 @@ pub(crate) fn thread_main() { } coarse_timestamp = get_timestamp(); - let should_update_smaps = opt::get().gather_maps && (force_smaps_update || (coarse_timestamp - last_smaps_update).as_msecs() >= 1000); + let should_update_smaps = opt::get().gather_maps + && (force_smaps_update || (coarse_timestamp - last_smaps_update).as_msecs() >= 1000); if should_update_smaps { let timestamp = get_timestamp(); update_smaps( &mut smaps_state, &mut backtrace_cache, &mut *serializer, - false + false, ); last_smaps_update = timestamp; @@ -830,15 +911,15 @@ pub(crate) fn thread_main() { &mut smaps_state, &mut backtrace_cache, &mut output_writer, - true + true, ); } let _ = output_writer.flush(); for client in &mut output_writer.inner_mut_without_flush().clients { - let _ = Response::Finished.write_to_stream( &mut client.stream ); + let _ = Response::Finished.write_to_stream(&mut client.stream); let _ = client.stream.flush(); } - info!( "Event thread finished" ); + info!("Event thread finished"); } diff --git a/preload/src/raw_file.rs b/preload/src/raw_file.rs index f5a3fd0c..47e50efb 100644 --- a/preload/src/raw_file.rs +++ b/preload/src/raw_file.rs @@ -1,66 +1,68 @@ -use std::path::Path; -use std::io::{self, Write}; +use libc; use std::ffi::CStr; -use std::os::unix::io::{IntoRawFd, FromRawFd, AsRawFd, RawFd}; +use std::io::{self, Write}; use std::mem; -use libc; +use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; +use std::path::Path; use crate::syscall; use crate::utils::stack_null_terminate; #[repr(transparent)] pub struct RawFile { - fd: libc::c_int + fd: libc::c_int, } impl RawFile { - pub fn borrow_raw( fd: &libc::c_int ) -> &RawFile { + pub fn borrow_raw(fd: &libc::c_int) -> &RawFile { unsafe { &*(fd as *const libc::c_int as *const RawFile) } } - pub fn create< P: AsRef< Path > >( path: P, permissions: libc::c_int ) -> Result< Self, io::Error > { + pub fn create>(path: P, permissions: libc::c_int) -> Result { let path = path.as_ref(); - let fd = stack_null_terminate( path.to_str().unwrap().as_bytes(), |path| { - let path = CStr::from_bytes_with_nul( path ).unwrap(); - syscall::open( path, libc::O_CLOEXEC | libc::O_CREAT | libc::O_TRUNC | libc::O_WRONLY, permissions ) + let fd = stack_null_terminate(path.to_str().unwrap().as_bytes(), |path| { + let path = CStr::from_bytes_with_nul(path).unwrap(); + syscall::open( + path, + libc::O_CLOEXEC | libc::O_CREAT | libc::O_TRUNC | libc::O_WRONLY, + permissions, + ) }); if fd < 0 { - return Err( io::Error::from_raw_os_error( fd ) ); + return Err(io::Error::from_raw_os_error(fd)); } - let fp = RawFile { - fd - }; + let fp = RawFile { fd }; - Ok( fp ) + Ok(fp) } - pub fn chmod( &self, permissions: libc::mode_t ) { - syscall::fchmod( self.fd, permissions ); + pub fn chmod(&self, permissions: libc::mode_t) { + syscall::fchmod(self.fd, permissions); } } impl Drop for RawFile { #[inline] - fn drop( &mut self ) { - syscall::close( self.fd ); + fn drop(&mut self) { + syscall::close(self.fd); } } impl AsRawFd for RawFile { #[inline] - fn as_raw_fd( &self ) -> RawFd { + fn as_raw_fd(&self) -> RawFd { self.fd } } impl IntoRawFd for RawFile { #[inline] - fn into_raw_fd( self ) -> RawFd { + fn into_raw_fd(self) -> RawFd { let fd = self.fd; - mem::forget( self ); + mem::forget(self); fd } @@ -68,55 +70,55 @@ impl IntoRawFd for RawFile { impl FromRawFd for RawFile { #[inline] - unsafe fn from_raw_fd( fd: RawFd ) -> Self { + unsafe fn from_raw_fd(fd: RawFd) -> Self { RawFile { fd } } } impl Write for RawFile { #[inline] - fn write( &mut self, buffer: &[u8] ) -> Result< usize, io::Error > { - <&RawFile as Write>::write( &mut &*self, buffer ) + fn write(&mut self, buffer: &[u8]) -> Result { + <&RawFile as Write>::write(&mut &*self, buffer) } #[inline] - fn flush( &mut self ) -> Result< (), io::Error > { - <&RawFile as Write>::flush( &mut &*self ) + fn flush(&mut self) -> Result<(), io::Error> { + <&RawFile as Write>::flush(&mut &*self) } } -impl< 'a > Write for &'a RawFile { +impl<'a> Write for &'a RawFile { #[inline] - fn write( &mut self, buffer: &[u8] ) -> Result< usize, io::Error > { - let count = syscall::write( self.fd, buffer ); + fn write(&mut self, buffer: &[u8]) -> Result { + let count = syscall::write(self.fd, buffer); if count < 0 { - Err( io::Error::from_raw_os_error( count as _ ) ) + Err(io::Error::from_raw_os_error(count as _)) } else { - Ok( count as _ ) + Ok(count as _) } } #[inline] - fn flush( &mut self ) -> Result< (), io::Error > { + fn flush(&mut self) -> Result<(), io::Error> { Ok(()) } } -pub fn rename< S: AsRef< Path >, D: AsRef< Path > >( src: S, dst: D ) -> Result< (), io::Error > { +pub fn rename, D: AsRef>(src: S, dst: D) -> Result<(), io::Error> { let src = src.as_ref(); let dst = dst.as_ref(); - let errcode = stack_null_terminate( src.to_str().unwrap().as_bytes(), |src| { - let src = CStr::from_bytes_with_nul( src ).unwrap(); - stack_null_terminate( dst.to_str().unwrap().as_bytes(), |dst| { - let dst = CStr::from_bytes_with_nul( dst ).unwrap(); - syscall::rename( src, dst ) + let errcode = stack_null_terminate(src.to_str().unwrap().as_bytes(), |src| { + let src = CStr::from_bytes_with_nul(src).unwrap(); + stack_null_terminate(dst.to_str().unwrap().as_bytes(), |dst| { + let dst = CStr::from_bytes_with_nul(dst).unwrap(); + syscall::rename(src, dst) }) }); if errcode == 0 { Ok(()) } else { - Err( io::Error::from_raw_os_error( errcode as _ ) ) + Err(io::Error::from_raw_os_error(errcode as _)) } } diff --git a/preload/src/smaps.rs b/preload/src/smaps.rs index 148a6a31..72eac58d 100644 --- a/preload/src/smaps.rs +++ b/preload/src/smaps.rs @@ -1,118 +1,129 @@ -use crate::utils::{HashMap, HashSet}; -use std::ops::Range; -use std::io::{Read, Write}; -use std::borrow::Cow; -use common::event::{ - RegionFlags, - Event -}; -use common::speedy::Writable; -use crate::timestamp::Timestamp; use crate::processing_thread::BacktraceCache; +use crate::timestamp::Timestamp; use crate::unwind::Backtrace; +use crate::utils::{HashMap, HashSet}; +use common::event::{Event, RegionFlags}; +use common::speedy::Writable; +use std::borrow::Cow; +use std::io::{Read, Write}; +use std::ops::Range; #[derive(Copy, Clone, PartialEq, Eq)] #[repr(u8)] pub enum MapKind { Mmap = 0, Jemalloc = 1, - Glibc = 2 + Glibc = 2, } impl MapKind { - fn as_str( self ) -> &'static str { + fn as_str(self) -> &'static str { match self { MapKind::Mmap => "mmap", MapKind::Jemalloc => "jemalloc", - MapKind::Glibc => "glibc" + MapKind::Glibc => "glibc", } } } #[derive(Copy, Clone)] #[repr(transparent)] -struct CompactName( u64 ); +struct CompactName(u64); impl CompactName { - fn new( id: u64, kind: MapKind ) -> Self { - Self( (id << 2) | (kind as u8 as u64) ) + fn new(id: u64, kind: MapKind) -> Self { + Self((id << 2) | (kind as u8 as u64)) } - fn id( self ) -> u64 { + fn id(self) -> u64 { self.0 >> 2 } - fn kind( self ) -> MapKind { - unsafe { std::mem::transmute( (self.0 & 0b11) as u8 ) } + fn kind(self) -> MapKind { + unsafe { std::mem::transmute((self.0 & 0b11) as u8) } } } #[test] fn test_compact_name() { - assert_eq!( CompactName::new( 0x1234FF, MapKind::Mmap ).id(), 0x1234FF ); - assert_eq!( CompactName::new( 0x1234FF, MapKind::Mmap ).kind(), MapKind::Mmap ); - assert_eq!( CompactName::new( 0x1234FF, MapKind::Mmap ).kind().as_str(), "mmap" ); - assert_eq!( CompactName::new( 0x1234FF, MapKind::Jemalloc ).id(), 0x1234FF ); - assert_eq!( CompactName::new( 0x1234FF, MapKind::Jemalloc ).kind(), MapKind::Jemalloc ); - assert_eq!( CompactName::new( 0x1234FF, MapKind::Jemalloc ).kind().as_str(), "jemalloc" ); + assert_eq!(CompactName::new(0x1234FF, MapKind::Mmap).id(), 0x1234FF); + assert_eq!( + CompactName::new(0x1234FF, MapKind::Mmap).kind(), + MapKind::Mmap + ); + assert_eq!( + CompactName::new(0x1234FF, MapKind::Mmap).kind().as_str(), + "mmap" + ); + assert_eq!(CompactName::new(0x1234FF, MapKind::Jemalloc).id(), 0x1234FF); + assert_eq!( + CompactName::new(0x1234FF, MapKind::Jemalloc).kind(), + MapKind::Jemalloc + ); + assert_eq!( + CompactName::new(0x1234FF, MapKind::Jemalloc) + .kind() + .as_str(), + "jemalloc" + ); } #[inline(always)] -pub fn construct_name( id: u64, kind: &str ) -> crate::utils::Buffer< 32 > { - let mut buffer = crate::utils::Buffer::< 32 >::new(); - write!( &mut buffer, "{}::{}\0", kind, id ).unwrap(); +pub fn construct_name(id: u64, kind: &str) -> crate::utils::Buffer<32> { + let mut buffer = crate::utils::Buffer::<32>::new(); + write!(&mut buffer, "{}::{}\0", kind, id).unwrap(); buffer } -const CULLING_THRESHOLD: Timestamp = Timestamp::from_secs( 1 ); +const CULLING_THRESHOLD: Timestamp = Timestamp::from_secs(1); -fn get_until< 'a >( p: &mut &'a str, delimiter: char ) -> &'a str { +fn get_until<'a>(p: &mut &'a str, delimiter: char) -> &'a str { let mut found = None; for (index, ch) in p.char_indices() { if ch == delimiter { - found = Some( index ); + found = Some(index); break; } } - if let Some( index ) = found { - let (before, after) = p.split_at( index ); - *p = &after[ delimiter.len_utf8().. ]; + if let Some(index) = found { + let (before, after) = p.split_at(index); + *p = &after[delimiter.len_utf8()..]; before } else { let before = *p; *p = ""; before - } - } + } +} -fn skip_whitespace( p: &mut &str ) { - while let Some( ch ) = p.chars().next() { +fn skip_whitespace(p: &mut &str) { + while let Some(ch) = p.chars().next() { if ch == ' ' { - *p = &p[ ch.len_utf8().. ]; + *p = &p[ch.len_utf8()..]; } else { break; } } } -fn get_char( p: &mut &str ) -> Option< char > { +fn get_char(p: &mut &str) -> Option { let ch = p.chars().next()?; - *p = &p[ ch.len_utf8().. ]; - Some( ch ) + *p = &p[ch.len_utf8()..]; + Some(ch) } #[derive(Clone)] pub struct MapSource { pub timestamp: Timestamp, pub backtrace: Backtrace, - pub tid: u32 + pub tid: u32, } #[derive(Clone)] struct MapBucket { id: u64, - source: MapSource + source: MapSource, } struct Mmap { @@ -124,15 +135,15 @@ struct Mmap { mmap_flags: u32, file_descriptor: u32, offset: u64, - source: MapSource + source: MapSource, } pub struct MapsRegistry { - emulated_vma_name_map: fast_range_map::RangeMap< CompactName >, + emulated_vma_name_map: fast_range_map::RangeMap, - mmap_by_address: fast_range_map::RangeMap< MapBucket >, - munmap_by_address: fast_range_map::RangeMap< MapBucket >, - mmaps: HashMap< u64, Mmap >, + mmap_by_address: fast_range_map::RangeMap, + munmap_by_address: fast_range_map::RangeMap, + mmaps: HashMap, } impl MapsRegistry { @@ -145,103 +156,163 @@ impl MapsRegistry { } } - pub fn set_vma_name( &mut self, pointer: *mut libc::c_void, length: usize, id: u64, kind: MapKind ) { + pub fn set_vma_name( + &mut self, + pointer: *mut libc::c_void, + length: usize, + id: u64, + kind: MapKind, + ) { if crate::global::is_pr_set_vma_anon_name_supported() { - let name = construct_name( id, kind.as_str() ); + let name = construct_name(id, kind.as_str()); unsafe { - crate::syscall::pr_set_vma_anon_name( pointer, length, &name ); + crate::syscall::pr_set_vma_anon_name(pointer, length, &name); } } else { - self.set_vma_name_slow( pointer, length, CompactName::new( id, kind ) ); + self.set_vma_name_slow(pointer, length, CompactName::new(id, kind)); } } - fn set_vma_name_slow( &mut self, pointer: *const libc::c_void, length: usize, name: CompactName ) { - self.emulated_vma_name_map.insert( pointer as u64..pointer as u64 + length as u64, name ); + fn set_vma_name_slow( + &mut self, + pointer: *const libc::c_void, + length: usize, + name: CompactName, + ) { + self.emulated_vma_name_map + .insert(pointer as u64..pointer as u64 + length as u64, name); } - pub fn clear_vma_name( &mut self, pointer: *mut libc::c_void, length: usize ) { + pub fn clear_vma_name(&mut self, pointer: *mut libc::c_void, length: usize) { if !crate::global::is_pr_set_vma_anon_name_supported() { - self.emulated_vma_name_map.remove( pointer as u64..pointer as u64 + length as u64 ); + self.emulated_vma_name_map + .remove(pointer as u64..pointer as u64 + length as u64); } } pub fn on_mmap( &mut self, id: u64, - range: Range< u64 >, + range: Range, source: MapSource, requested_address: u64, mmap_protection: u32, mmap_flags: u32, file_descriptor: u32, - offset: u64 + offset: u64, ) { - for (range_unmapped, original_bucket) in self.mmap_by_address.remove( range.clone() ) { + for (range_unmapped, original_bucket) in self.mmap_by_address.remove(range.clone()) { // When called with MAP_FIXED the `mmap` can also act as an `munmap`. - let bucket = MapBucket { id: original_bucket.id, source: source.clone() }; + let bucket = MapBucket { + id: original_bucket.id, + source: source.clone(), + }; // If there were already any unmaps in this range leave them alone. They were first, so they should take precendence. - let existing_unmaps: smallvec::SmallVec< [Range< u64 >; 2] > = self.munmap_by_address.get_in_range( range_unmapped.clone() ).map( |(range, _)| range.clone() ).collect(); + let existing_unmaps: smallvec::SmallVec<[Range; 2]> = self + .munmap_by_address + .get_in_range(range_unmapped.clone()) + .map(|(range, _)| range.clone()) + .collect(); // Insert an unmap everywhere which was *not* already unmapped. let mut start = range_unmapped.start; for existing_unmap in existing_unmaps { - trace!( "On munmap through fixed mmap: {:016X}..{:016X}, old_id = {}, new_id = {}", start, existing_unmap.start, original_bucket.id, id ); - self.munmap_by_address.insert( start..existing_unmap.start, bucket.clone() ); + trace!( + "On munmap through fixed mmap: {:016X}..{:016X}, old_id = {}, new_id = {}", + start, + existing_unmap.start, + original_bucket.id, + id + ); + self.munmap_by_address + .insert(start..existing_unmap.start, bucket.clone()); start = existing_unmap.end; } - trace!( "On munmap through fixed mmap: {:016X}..{:016X}, old_id = {}, new_id = {}", start, range_unmapped.end, original_bucket.id, id ); - self.munmap_by_address.insert( start..range_unmapped.end, bucket.clone() ); + trace!( + "On munmap through fixed mmap: {:016X}..{:016X}, old_id = {}, new_id = {}", + start, + range_unmapped.end, + original_bucket.id, + id + ); + self.munmap_by_address + .insert(start..range_unmapped.end, bucket.clone()); } let bucket = MapBucket { id, - source: source.clone() + source: source.clone(), }; - trace!( "On mmap: 0x{:016X}..0x{:016X}, id = {}, pages = {}", range.start, range.end, id, (range.end - range.start) / 4096 ); - self.mmap_by_address.insert( range.clone(), bucket ); - self.mmaps.insert( id, Mmap { + trace!( + "On mmap: 0x{:016X}..0x{:016X}, id = {}, pages = {}", + range.start, + range.end, id, - address: range.start, - requested_address, - requested_length: range.end - range.start, - mmap_protection, - mmap_flags, - file_descriptor, - offset, - source - }); + (range.end - range.start) / 4096 + ); + self.mmap_by_address.insert(range.clone(), bucket); + self.mmaps.insert( + id, + Mmap { + id, + address: range.start, + requested_address, + requested_length: range.end - range.start, + mmap_protection, + mmap_flags, + file_descriptor, + offset, + source, + }, + ); } - pub fn on_munmap( &mut self, range: Range< u64 >, source: MapSource ) { - trace!( "On mummap: 0x{:016X}..0x{:016X}, pages = {}", range.start, range.end, (range.end - range.start) / 4096 ); + pub fn on_munmap(&mut self, range: Range, source: MapSource) { + trace!( + "On mummap: 0x{:016X}..0x{:016X}, pages = {}", + range.start, + range.end, + (range.end - range.start) / 4096 + ); if !crate::global::is_pr_set_vma_anon_name_supported() { - self.emulated_vma_name_map.remove( range.clone() ); + self.emulated_vma_name_map.remove(range.clone()); } - for (removed_range, bucket) in self.mmap_by_address.remove( range ) { - trace!( " Removed chunk: 0x{:016X}..0x{:016X}, id = {}, pages = {}", removed_range.start, removed_range.end, bucket.id, (removed_range.end - removed_range.start) / 4096 ); - self.munmap_by_address.insert( removed_range, MapBucket { id: bucket.id, source: source.clone() } ); + for (removed_range, bucket) in self.mmap_by_address.remove(range) { + trace!( + " Removed chunk: 0x{:016X}..0x{:016X}, id = {}, pages = {}", + removed_range.start, + removed_range.end, + bucket.id, + (removed_range.end - removed_range.start) / 4096 + ); + self.munmap_by_address.insert( + removed_range, + MapBucket { + id: bucket.id, + source: source.clone(), + }, + ); } } } -type RegionVec = smallvec::SmallVec< [Region; 1] >; -type SourcesVec = smallvec::SmallVec< [RegionRemovalSource; 1] >; +type RegionVec = smallvec::SmallVec<[Region; 1]>; +type SourcesVec = smallvec::SmallVec<[RegionRemovalSource; 1]>; struct Map { - regions: RegionVec + regions: RegionVec, } enum PendingEvent { Mmap { epoch: u64, - mmap: Mmap + mmap: Mmap, }, AddRegion { timestamp: Timestamp, @@ -257,7 +328,7 @@ enum PendingEvent { id: u64, address: u64, length: u64, - usage: RegionUsage + usage: RegionUsage, }, RemoveRegion { timestamp: Timestamp, @@ -265,13 +336,13 @@ enum PendingEvent { id: u64, address: u64, length: u64, - sources: SourcesVec - } + sources: SourcesVec, + }, } struct PendingMap { earliest_timestamp: Timestamp, - events: smallvec::SmallVec< [PendingEvent; 1] > + events: smallvec::SmallVec<[PendingEvent; 1]>, } #[derive(Clone, PartialEq, Eq, Hash, Debug)] @@ -305,40 +376,40 @@ struct Region { struct RegionRemovalSource { address: u64, length: u64, - source: MapSource + source: MapSource, } #[derive(Default)] pub struct State { - tmp_mmap_by_address: fast_range_map::RangeMap< MapBucket >, - tmp_munmap_by_address: fast_range_map::RangeMap< MapBucket >, - tmp_mmaps: HashMap< u64, Mmap >, - tmp_buffer: Vec< u8 >, - tmp_found_maps: HashMap< u64, RegionVec >, - tmp_new_map_by_id: HashMap< u64, Map >, - tmp_all_new_events: Vec< PendingEvent >, - tmp_emulated_vma_name_map: fast_range_map::RangeMap< CompactName >, - tmp_padding_maps: Vec< (u64, u64) >, - tmp_seen_maps: HashSet< u64 >, - - map_by_id: HashMap< u64, Map >, - pending: HashMap< u64, PendingMap >, + tmp_mmap_by_address: fast_range_map::RangeMap, + tmp_munmap_by_address: fast_range_map::RangeMap, + tmp_mmaps: HashMap, + tmp_buffer: Vec, + tmp_found_maps: HashMap, + tmp_new_map_by_id: HashMap, + tmp_all_new_events: Vec, + tmp_emulated_vma_name_map: fast_range_map::RangeMap, + tmp_padding_maps: Vec<(u64, u64)>, + tmp_seen_maps: HashSet, + + map_by_id: HashMap, + pending: HashMap, epoch: u64, } impl State { pub fn new_preallocated() -> Self { let mut state = Self::default(); - state.tmp_buffer.reserve( 64 * 1024 ); + state.tmp_buffer.reserve(64 * 1024); if !crate::global::is_pr_set_vma_anon_name_supported() { - state.tmp_padding_maps.reserve( 1024 ); - state.tmp_seen_maps.reserve( 4096 ); + state.tmp_padding_maps.reserve(1024); + state.tmp_seen_maps.reserve(4096); } state } - fn clear_ephemeral( &mut self ) { + fn clear_ephemeral(&mut self) { self.tmp_mmap_by_address.clear(); self.tmp_munmap_by_address.clear(); self.tmp_mmaps.clear(); @@ -352,16 +423,18 @@ impl State { } } -fn emit_mmap( - mmap: Mmap, - backtrace_cache: &mut BacktraceCache, - serializer: &mut impl Write -) { - let backtrace = crate::writers::write_backtrace( &mut *serializer, mmap.source.backtrace.clone(), backtrace_cache ).ok().unwrap_or( 0 ); +fn emit_mmap(mmap: Mmap, backtrace_cache: &mut BacktraceCache, serializer: &mut impl Write) { + let backtrace = crate::writers::write_backtrace( + &mut *serializer, + mmap.source.backtrace.clone(), + backtrace_cache, + ) + .ok() + .unwrap_or(0); let source = common::event::RegionSource { timestamp: mmap.source.timestamp, backtrace, - thread: mmap.source.tid + thread: mmap.source.tid, }; let _ = Event::MemoryMapEx { @@ -373,8 +446,9 @@ fn emit_mmap( mmap_flags: mmap.mmap_flags, file_descriptor: mmap.file_descriptor, offset: mmap.offset, - source - }.write_to_stream( &mut *serializer ); + source, + } + .write_to_stream(&mut *serializer); } fn emit_add_region( @@ -383,7 +457,7 @@ fn emit_add_region( info: &RegionInfo, flags: RegionFlags, name: &str, - serializer: &mut impl Write + serializer: &mut impl Write, ) { let _ = Event::AddRegion { timestamp, @@ -395,8 +469,9 @@ fn emit_add_region( major: info.major, minor: info.minor, flags, - name: name.into() - }.write_to_stream( &mut *serializer ); + name: name.into(), + } + .write_to_stream(&mut *serializer); } fn emit_usage( @@ -405,7 +480,7 @@ fn emit_usage( length: u64, timestamp: Timestamp, usage: RegionUsage, - serializer: &mut impl Write + serializer: &mut impl Write, ) { let _ = Event::UpdateRegionUsage { timestamp, @@ -418,7 +493,8 @@ fn emit_usage( private_clean: usage.private_clean, private_dirty: usage.private_dirty, swap: usage.swap, - }.write_to_stream( &mut *serializer ); + } + .write_to_stream(&mut *serializer); } fn emit_remove_region( @@ -428,61 +504,78 @@ fn emit_remove_region( length: u64, sources: SourcesVec, backtrace_cache: &mut BacktraceCache, - serializer: &mut impl Write + serializer: &mut impl Write, ) { - let sources_out: smallvec::SmallVec< [common::event::RegionTargetedSource; 1] > = sources.into_iter().map( |source| { - let backtrace = crate::writers::write_backtrace( &mut *serializer, source.source.backtrace.clone(), backtrace_cache ).ok().unwrap_or( 0 ); - common::event::RegionTargetedSource { - address: source.address, - length: source.length, - source: common::event::RegionSource { - timestamp: source.source.timestamp, - backtrace, - thread: source.source.tid + let sources_out: smallvec::SmallVec<[common::event::RegionTargetedSource; 1]> = sources + .into_iter() + .map(|source| { + let backtrace = crate::writers::write_backtrace( + &mut *serializer, + source.source.backtrace.clone(), + backtrace_cache, + ) + .ok() + .unwrap_or(0); + common::event::RegionTargetedSource { + address: source.address, + length: source.length, + source: common::event::RegionSource { + timestamp: source.source.timestamp, + backtrace, + thread: source.source.tid, + }, } - } - }).collect(); + }) + .collect(); let _ = Event::RemoveRegion { timestamp, map_id, address, length, - sources: Cow::Borrowed( &sources_out ) - }.write_to_stream( &mut *serializer ); + sources: Cow::Borrowed(&sources_out), + } + .write_to_stream(&mut *serializer); } -fn emit_events( backtrace_cache: &mut BacktraceCache, serializer: &mut impl Write, new_events: impl IntoIterator< Item = PendingEvent > ) { +fn emit_events( + backtrace_cache: &mut BacktraceCache, + serializer: &mut impl Write, + new_events: impl IntoIterator, +) { for event in new_events { match event { PendingEvent::Mmap { mmap, .. } => { - emit_mmap( - mmap, - backtrace_cache, - serializer - ); - }, - PendingEvent::AddRegion { timestamp, id, ref info, flags, name, .. } => { - emit_add_region( - timestamp, - id, - info, - flags, - name.as_str(), - serializer - ); - }, - PendingEvent::UpdateUsage { id, timestamp, address, length, usage, .. } => { - emit_usage( - id, - address, - length, - timestamp, - usage, - serializer - ); - }, - PendingEvent::RemoveRegion { timestamp, id, address, length, sources, .. } => { + emit_mmap(mmap, backtrace_cache, serializer); + } + PendingEvent::AddRegion { + timestamp, + id, + ref info, + flags, + name, + .. + } => { + emit_add_region(timestamp, id, info, flags, name.as_str(), serializer); + } + PendingEvent::UpdateUsage { + id, + timestamp, + address, + length, + usage, + .. + } => { + emit_usage(id, address, length, timestamp, usage, serializer); + } + PendingEvent::RemoveRegion { + timestamp, + id, + address, + length, + sources, + .. + } => { emit_remove_region( timestamp, id, @@ -490,7 +583,7 @@ fn emit_events( backtrace_cache: &mut BacktraceCache, serializer: &mut impl Writ length, sources, backtrace_cache, - serializer + serializer, ); } } @@ -498,12 +591,12 @@ fn emit_events( backtrace_cache: &mut BacktraceCache, serializer: &mut impl Writ } fn generate_unmaps( - tmp_munmap_by_address: &fast_range_map::RangeMap< MapBucket >, + tmp_munmap_by_address: &fast_range_map::RangeMap, timestamp: Timestamp, epoch: u64, id: u64, old_region: &Region, - output: &mut Vec< PendingEvent > + output: &mut Vec, ) { let address_start = old_region.info.address; let address_end = old_region.info.address + old_region.info.length; @@ -511,23 +604,31 @@ fn generate_unmaps( let mut sources = SourcesVec::new(); // Let's try to find which calls resulted in its disappearance. - for (unmap_range, unmap_bucket) in tmp_munmap_by_address.get_in_range( address_start..address_end ) { - trace!( "Found a source for an unmap: 0x{:016X}..0x{:016X}, id = {}, pages = {}", unmap_range.start, unmap_range.end, id, (unmap_range.end - unmap_range.start) / 4096 ); + for (unmap_range, unmap_bucket) in + tmp_munmap_by_address.get_in_range(address_start..address_end) + { + trace!( + "Found a source for an unmap: 0x{:016X}..0x{:016X}, id = {}, pages = {}", + unmap_range.start, + unmap_range.end, + id, + (unmap_range.end - unmap_range.start) / 4096 + ); - sources.push( RegionRemovalSource { + sources.push(RegionRemovalSource { address: unmap_range.start, length: unmap_range.end - unmap_range.start, - source: unmap_bucket.source.clone() + source: unmap_bucket.source.clone(), }); } - output.push( PendingEvent::RemoveRegion { + output.push(PendingEvent::RemoveRegion { timestamp, epoch, id, address: old_region.info.address, length: old_region.info.length, - sources + sources, }); } @@ -537,7 +638,7 @@ pub fn update_smaps( serializer: &mut impl Write, force_emit: bool, ) { - trace!( "Scanning smaps..." ); + trace!("Scanning smaps..."); state.clear_ephemeral(); state.epoch += 1; @@ -546,96 +647,130 @@ pub fn update_smaps( { let mut maps_registry = crate::global::MMAP_REGISTRY.lock().unwrap(); - maps_registry.mmap_by_address.clone_into( &mut state.tmp_mmap_by_address ); - std::mem::swap( &mut maps_registry.munmap_by_address, &mut state.tmp_munmap_by_address ); + maps_registry + .mmap_by_address + .clone_into(&mut state.tmp_mmap_by_address); + std::mem::swap( + &mut maps_registry.munmap_by_address, + &mut state.tmp_munmap_by_address, + ); maps_registry.munmap_by_address.clear(); - std::mem::swap( &mut maps_registry.mmaps, &mut state.tmp_mmaps ); + std::mem::swap(&mut maps_registry.mmaps, &mut state.tmp_mmaps); maps_registry.mmaps.clear(); - maps_registry.emulated_vma_name_map.clone_into( &mut state.tmp_emulated_vma_name_map ); + maps_registry + .emulated_vma_name_map + .clone_into(&mut state.tmp_emulated_vma_name_map); timestamp = crate::timestamp::get_timestamp(); - let mut fp = std::fs::File::open( "/proc/self/smaps" ).expect( "failed to open smaps" ); - fp.read_to_end( &mut state.tmp_buffer ).expect( "failed to read smaps" ); + let mut fp = std::fs::File::open("/proc/self/smaps").expect("failed to open smaps"); + fp.read_to_end(&mut state.tmp_buffer) + .expect("failed to read smaps"); - std::mem::drop( maps_registry ); - std::mem::drop( fp ); + std::mem::drop(maps_registry); + std::mem::drop(fp); }; - let smaps = std::str::from_utf8( &state.tmp_buffer ).expect( "failed to parse smaps as UTF-8" ); // TODO: This is probably not always true. + let smaps = std::str::from_utf8(&state.tmp_buffer).expect("failed to parse smaps as UTF-8"); // TODO: This is probably not always true. - let region_info_to_id: HashMap< RegionInfo, (u64, usize) > = - state.map_by_id.iter() - .flat_map( |(id, map)| std::iter::once( id ).cycle().zip( map.regions.iter().enumerate() ) ) - .map( |(id, (region_index, region))| (region.info.clone(), (*id, region_index)) ) - .collect(); + let region_info_to_id: HashMap = state + .map_by_id + .iter() + .flat_map(|(id, map)| { + std::iter::once(id) + .cycle() + .zip(map.regions.iter().enumerate()) + }) + .map(|(id, (region_index, region))| (region.info.clone(), (*id, region_index))) + .collect(); - let mut lines = smaps.trim().split( "\n" ).peekable(); + let mut lines = smaps.trim().split("\n").peekable(); loop { let mut line = match lines.next() { - Some( line ) => line, - None => break + Some(line) => line, + None => break, + }; + + let address = u64::from_str_radix(get_until(&mut line, '-'), 16).unwrap(); + let address_end = u64::from_str_radix(get_until(&mut line, ' '), 16).unwrap(); + let is_readable = if get_char(&mut line).unwrap() == 'r' { + RegionFlags::READABLE + } else { + RegionFlags::empty() + }; + let is_writable = if get_char(&mut line).unwrap() == 'w' { + RegionFlags::WRITABLE + } else { + RegionFlags::empty() + }; + let is_executable = if get_char(&mut line).unwrap() == 'x' { + RegionFlags::EXECUTABLE + } else { + RegionFlags::empty() }; + let is_shared = if get_char(&mut line).unwrap() == 's' { + RegionFlags::SHARED + } else { + RegionFlags::empty() + }; + get_char(&mut line); - let address = u64::from_str_radix( get_until( &mut line, '-' ), 16 ).unwrap(); - let address_end = u64::from_str_radix( get_until( &mut line, ' ' ), 16 ).unwrap(); - let is_readable = if get_char( &mut line ).unwrap() == 'r' { RegionFlags::READABLE } else { RegionFlags::empty() }; - let is_writable = if get_char( &mut line ).unwrap() == 'w' { RegionFlags::WRITABLE } else { RegionFlags::empty() }; - let is_executable = if get_char( &mut line ).unwrap() == 'x' { RegionFlags::EXECUTABLE } else { RegionFlags::empty() }; - let is_shared = if get_char( &mut line ).unwrap() == 's' { RegionFlags::SHARED } else { RegionFlags::empty() }; - get_char( &mut line ); - - let file_offset = u64::from_str_radix( get_until( &mut line, ' ' ), 16 ).unwrap(); - let major = u32::from_str_radix( get_until( &mut line, ':' ), 16 ).unwrap(); - let minor = u32::from_str_radix( get_until( &mut line, ' ' ), 16 ).unwrap(); - let inode: u64 = get_until( &mut line, ' ' ).parse().unwrap(); - skip_whitespace( &mut line ); - let mut name = Cow::Borrowed( line ); - let mut id: Option< u64 > = None; + let file_offset = u64::from_str_radix(get_until(&mut line, ' '), 16).unwrap(); + let major = u32::from_str_radix(get_until(&mut line, ':'), 16).unwrap(); + let minor = u32::from_str_radix(get_until(&mut line, ' '), 16).unwrap(); + let inode: u64 = get_until(&mut line, ' ').parse().unwrap(); + skip_whitespace(&mut line); + let mut name = Cow::Borrowed(line); + let mut id: Option = None; const BYTEHOUND_MEMFD_PREFIX: &str = "/memfd:bytehound::"; const BYTEHOUND_MEMFD_PADDING: &str = "/memfd:bytehound_padding"; let mut is_padding = false; if !crate::global::is_pr_set_vma_anon_name_supported() { - if name.starts_with( BYTEHOUND_MEMFD_PADDING ) { - state.tmp_padding_maps.push( (address, address_end - address) ); + if name.starts_with(BYTEHOUND_MEMFD_PADDING) { + state + .tmp_padding_maps + .push((address, address_end - address)); is_padding = true; } else { - state.tmp_seen_maps.insert( address ); - state.tmp_seen_maps.insert( address_end - 1 ); + state.tmp_seen_maps.insert(address); + state.tmp_seen_maps.insert(address_end - 1); } } // Try to extract the ID we've packed into the name. - if crate::global::is_pr_set_vma_anon_name_supported() && name.starts_with( "[anon:" ) { + if crate::global::is_pr_set_vma_anon_name_supported() && name.starts_with("[anon:") { // A name set with PR_SET_VMA_ANON_NAME. - if let Some( index_1 ) = name.find( "::" ) { - if let Some( length ) = name[ index_1 + 2.. ].find( "]" ) { + if let Some(index_1) = name.find("::") { + if let Some(length) = name[index_1 + 2..].find("]") { let index_2 = index_1 + 2 + length; if index_2 + 1 == name.len() { - if let Ok( value ) = name[ index_1 + 2..index_2 ].parse() { - id = Some( value ); - let mut cleaned_name = String::with_capacity( index_1 + 1 ); - cleaned_name.push_str( &name[ ..index_1 ] ); - cleaned_name.push_str( "]" ); - name = Cow::Owned( cleaned_name ); + if let Ok(value) = name[index_1 + 2..index_2].parse() { + id = Some(value); + let mut cleaned_name = String::with_capacity(index_1 + 1); + cleaned_name.push_str(&name[..index_1]); + cleaned_name.push_str("]"); + name = Cow::Owned(cleaned_name); } } } } - } else if name.starts_with( BYTEHOUND_MEMFD_PREFIX ) { + } else if name.starts_with(BYTEHOUND_MEMFD_PREFIX) { let index_1 = BYTEHOUND_MEMFD_PREFIX.len(); - let index_2 = name[ index_1.. ].find( " " ).map( |index_2| index_1 + index_2 ).unwrap_or( name.len() ); - if let Ok( value ) = name[ index_1..index_2 ].parse() { - id = Some( value ); - name = Cow::Borrowed( "[anon:bytehound]" ); + let index_2 = name[index_1..] + .find(" ") + .map(|index_2| index_1 + index_2) + .unwrap_or(name.len()); + if let Ok(value) = name[index_1..index_2].parse() { + id = Some(value); + name = Cow::Borrowed("[anon:bytehound]"); } - } else if let Some( (_, compact_name) ) = state.tmp_emulated_vma_name_map.get( address ) { + } else if let Some((_, compact_name)) = state.tmp_emulated_vma_name_map.get(address) { if name != "anon_inode:[perf_event]" { - name = Cow::Owned( format!( "[anon:{}]", compact_name.kind().as_str() ) ); + name = Cow::Owned(format!("[anon:{}]", compact_name.kind().as_str())); } - id = Some( compact_name.id() ); + id = Some(compact_name.id()); } let info = RegionInfo { @@ -644,7 +779,7 @@ pub fn update_smaps( file_offset, inode, major, - minor + minor, }; let flags = is_readable | is_writable | is_executable | is_shared; @@ -656,15 +791,15 @@ pub fn update_smaps( let mut private_dirty = 0; let mut anonymous = 0; let mut swap = 0; - while let Some( line ) = lines.peek() { + while let Some(line) = lines.peek() { let mut line = *line; - let key = get_until( &mut line, ':' ); - if key.as_bytes().contains( &b' ' ) { + let key = get_until(&mut line, ':'); + if key.as_bytes().contains(&b' ') { break; } - skip_whitespace( &mut line ); - let value = get_until( &mut line, ' ' ); + skip_whitespace(&mut line); + let value = get_until(&mut line, ' '); match key { "Rss" => rss = value.parse().unwrap(), @@ -684,7 +819,10 @@ pub fn update_smaps( continue; } - debug_assert_eq!( rss, shared_clean + shared_dirty + private_clean + private_dirty ); + debug_assert_eq!( + rss, + shared_clean + shared_dirty + private_clean + private_dirty + ); let usage = RegionUsage { anonymous, @@ -700,16 +838,16 @@ pub fn update_smaps( // // This can happen if the name was changed by the application itself, or if it's just simply // a map which was mmaped outside of our control. - if let Some( &(map_id, region_index) ) = region_info_to_id.get( &info ) { - if state.map_by_id.get( &map_id ).unwrap().regions[ region_index ].name == name { - id = Some( map_id ); + if let Some(&(map_id, region_index)) = region_info_to_id.get(&info) { + if state.map_by_id.get(&map_id).unwrap().regions[region_index].name == name { + id = Some(map_id); } } // TODO: Handle maps which were split due to e.g. mprotect. } - let id = id.unwrap_or_else( || crate::global::next_map_id() ); + let id = id.unwrap_or_else(|| crate::global::next_map_id()); let region = Region { info, name: name.into(), @@ -717,42 +855,51 @@ pub fn update_smaps( last_usage: usage, }; - state.tmp_found_maps.entry( id ).or_insert_with( RegionVec::new ).push( region ); + state + .tmp_found_maps + .entry(id) + .or_insert_with(RegionVec::new) + .push(region); } for (id, new_regions) in state.tmp_found_maps.drain() { - match state.map_by_id.remove( &id ) { - Some( mut map ) => { + match state.map_by_id.remove(&id) { + Some(mut map) => { // This is an existing map. let mut new_events = Vec::new(); let mut merged_regions = RegionVec::new(); for new_region in new_regions { - if let Some( old_region_index ) = map.regions.iter().position( |old_region| old_region.info == new_region.info && old_region.name == new_region.name ) { + if let Some(old_region_index) = map.regions.iter().position(|old_region| { + old_region.info == new_region.info && old_region.name == new_region.name + }) { // This is an existing region. - let mut old_region = map.regions.swap_remove( old_region_index ); + let mut old_region = map.regions.swap_remove(old_region_index); if old_region.last_usage != new_region.last_usage { - new_events.push( PendingEvent::UpdateUsage { + new_events.push(PendingEvent::UpdateUsage { epoch: state.epoch, id, timestamp, address: new_region.info.address, length: new_region.info.length, - usage: new_region.last_usage.clone() + usage: new_region.last_usage.clone(), }); old_region.last_usage = new_region.last_usage; } // TODO: Handle flag changes. - merged_regions.push( old_region ); + merged_regions.push(old_region); } else { // This is a brand new region. trace!( "Found new region for an existing map: 0x{:016X}, id = {}, source = {}", new_region.info.address, id, - state.tmp_mmap_by_address.get_value( new_region.info.address ).is_some() + state + .tmp_mmap_by_address + .get_value(new_region.info.address) + .is_some() ); - new_events.push( PendingEvent::AddRegion { + new_events.push(PendingEvent::AddRegion { timestamp, epoch: state.epoch, id, @@ -760,19 +907,19 @@ pub fn update_smaps( flags: new_region.last_flags, name: new_region.name.clone(), }); - new_events.push( PendingEvent::UpdateUsage { + new_events.push(PendingEvent::UpdateUsage { epoch: state.epoch, id, timestamp, address: new_region.info.address, length: new_region.info.length, - usage: new_region.last_usage.clone() + usage: new_region.last_usage.clone(), }); - merged_regions.push( new_region ); + merged_regions.push(new_region); } } - for old_region in map.regions.drain( .. ) { + for old_region in map.regions.drain(..) { // This region doesn't exist anymore. generate_unmaps( @@ -781,37 +928,37 @@ pub fn update_smaps( state.epoch, id, &old_region, - &mut new_events + &mut new_events, ); } - std::mem::swap( &mut map.regions, &mut merged_regions ); + std::mem::swap(&mut map.regions, &mut merged_regions); - if let Some( pending ) = state.pending.get_mut( &id ) { + if let Some(pending) = state.pending.get_mut(&id) { // We haven't emitted this map yet. if timestamp - pending.earliest_timestamp < CULLING_THRESHOLD && !force_emit { // It still hasn't lived long enough to be emitted. - pending.events.extend( new_events.drain( .. ) ); + pending.events.extend(new_events.drain(..)); } else { // It has lived long enough; flush it. - state.tmp_all_new_events.extend( pending.events.drain( .. ) ); - state.pending.remove( &id ); + state.tmp_all_new_events.extend(pending.events.drain(..)); + state.pending.remove(&id); } } - state.tmp_all_new_events.extend( new_events.drain( .. ) ); - state.tmp_new_map_by_id.insert( id, map ); - }, + state.tmp_all_new_events.extend(new_events.drain(..)); + state.tmp_new_map_by_id.insert(id, map); + } None => { // This is a new map. let mut earliest_timestamp = timestamp; let mut events = smallvec::SmallVec::new(); - if let Some( mmap ) = state.tmp_mmaps.remove( &id ) { + if let Some(mmap) = state.tmp_mmaps.remove(&id) { earliest_timestamp = mmap.source.timestamp; - events.push( PendingEvent::Mmap { + events.push(PendingEvent::Mmap { epoch: state.epoch, - mmap + mmap, }); } @@ -821,10 +968,13 @@ pub fn update_smaps( region.info.address, region.info.address + region.info.length, id, - state.tmp_mmap_by_address.get_value( region.info.address ).is_some() + state + .tmp_mmap_by_address + .get_value(region.info.address) + .is_some() ); - events.push( PendingEvent::AddRegion { + events.push(PendingEvent::AddRegion { timestamp, epoch: state.epoch, id, @@ -832,28 +982,36 @@ pub fn update_smaps( flags: region.last_flags, name: region.name.clone(), }); - events.push( PendingEvent::UpdateUsage { + events.push(PendingEvent::UpdateUsage { epoch: state.epoch, id, timestamp, address: region.info.address, length: region.info.length, - usage: region.last_usage.clone() + usage: region.last_usage.clone(), }); } - state.pending.insert( id, PendingMap { - earliest_timestamp, - events - }); - state.tmp_new_map_by_id.insert( id, Map { regions: new_regions } ); + state.pending.insert( + id, + PendingMap { + earliest_timestamp, + events, + }, + ); + state.tmp_new_map_by_id.insert( + id, + Map { + regions: new_regions, + }, + ); } } } for (id, map) in state.map_by_id.drain() { // All of these maps were not picked up, which means they were all unmapped. - if state.pending.remove( &id ).is_some() { + if state.pending.remove(&id).is_some() { // This map was not emitted. continue; } @@ -865,36 +1023,64 @@ pub fn update_smaps( state.epoch, id, ®ion, - &mut state.tmp_all_new_events + &mut state.tmp_all_new_events, ); } } // Make sure any pending events are emitted in the proper order, and that the removals are prioritized. - state.tmp_all_new_events.sort_unstable_by_key( |event| { - match event { - PendingEvent::Mmap { epoch, mmap: Mmap { id, address, .. }, .. } => (*epoch, 1, *id, *address), - PendingEvent::AddRegion { epoch, id, info: RegionInfo { address, .. }, .. } => (*epoch, 2, *id, *address), - PendingEvent::UpdateUsage { epoch, id, address, .. } => (*epoch, 3, *id, *address), - PendingEvent::RemoveRegion { epoch, id, address, .. } => (*epoch, 0, *id, *address), - } - }); - - emit_events( backtrace_cache, serializer, state.tmp_all_new_events.drain( .. ) ); - std::mem::swap( &mut state.map_by_id, &mut state.tmp_new_map_by_id ); + state + .tmp_all_new_events + .sort_unstable_by_key(|event| match event { + PendingEvent::Mmap { + epoch, + mmap: Mmap { id, address, .. }, + .. + } => (*epoch, 1, *id, *address), + PendingEvent::AddRegion { + epoch, + id, + info: RegionInfo { address, .. }, + .. + } => (*epoch, 2, *id, *address), + PendingEvent::UpdateUsage { + epoch, id, address, .. + } => (*epoch, 3, *id, *address), + PendingEvent::RemoveRegion { + epoch, id, address, .. + } => (*epoch, 0, *id, *address), + }); - for (address, length) in state.tmp_padding_maps.drain( .. ) { - if state.tmp_seen_maps.contains( &(address - 1) ) || state.tmp_seen_maps.contains( &(address + length) ) { + emit_events( + backtrace_cache, + serializer, + state.tmp_all_new_events.drain(..), + ); + std::mem::swap(&mut state.map_by_id, &mut state.tmp_new_map_by_id); + + for (address, length) in state.tmp_padding_maps.drain(..) { + if state.tmp_seen_maps.contains(&(address - 1)) + || state.tmp_seen_maps.contains(&(address + length)) + { continue; } - trace!( "Unmapping a padding map at 0x{:016X}..0x{:016X}", address, address + length ); + trace!( + "Unmapping a padding map at 0x{:016X}..0x{:016X}", + address, + address + length + ); unsafe { - crate::syscall::munmap( address as *mut libc::c_void, length as libc::size_t ); + crate::syscall::munmap(address as *mut libc::c_void, length as libc::size_t); } } for (id, mmap) in state.tmp_mmaps.drain() { - debug!( "Map registered yet not found in smaps: 0x{:016X}..0x{:016X}, id = {}", mmap.address, mmap.address + mmap.requested_length, id ); + debug!( + "Map registered yet not found in smaps: 0x{:016X}..0x{:016X}, id = {}", + mmap.address, + mmap.address + mmap.requested_length, + id + ); } } diff --git a/preload/src/spin_lock.rs b/preload/src/spin_lock.rs index bc84229f..e32848d9 100644 --- a/preload/src/spin_lock.rs +++ b/preload/src/spin_lock.rs @@ -1,84 +1,87 @@ -use std::sync::atomic::{AtomicBool, Ordering}; -use std::ops::{Deref, DerefMut}; use std::cell::UnsafeCell; use std::mem::transmute; +use std::ops::{Deref, DerefMut}; +use std::sync::atomic::{AtomicBool, Ordering}; -pub struct SpinLock< T > { - pub value: UnsafeCell< T >, - pub flag: AtomicBool +pub struct SpinLock { + pub value: UnsafeCell, + pub flag: AtomicBool, } -unsafe impl< T > Send for SpinLock< T > where T: Send {} -unsafe impl< T > Sync for SpinLock< T > where T: Send {} +unsafe impl Send for SpinLock where T: Send {} +unsafe impl Sync for SpinLock where T: Send {} -pub struct SpinLockGuard< 'a, T: 'a >( &'a SpinLock< T > ); +pub struct SpinLockGuard<'a, T: 'a>(&'a SpinLock); -impl< T > SpinLock< T > { - pub const fn new( value: T ) -> Self { +impl SpinLock { + pub const fn new(value: T) -> Self { SpinLock { - value: UnsafeCell::new( value ), - flag: AtomicBool::new( false ) + value: UnsafeCell::new(value), + flag: AtomicBool::new(false), } } - pub fn lock( &self ) -> SpinLockGuard< T > { - while self.flag.compare_exchange_weak( false, true, Ordering::Acquire, Ordering::Acquire ).is_err() { - } + pub fn lock(&self) -> SpinLockGuard { + while self + .flag + .compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Acquire) + .is_err() + {} - SpinLockGuard( self ) + SpinLockGuard(self) } - pub fn try_lock( &self ) -> Option< SpinLockGuard< T > > { - if self.flag.compare_exchange( false, true, Ordering::Acquire, Ordering::Acquire ).is_ok() { - Some( SpinLockGuard( self ) ) + pub fn try_lock(&self) -> Option> { + if self + .flag + .compare_exchange(false, true, Ordering::Acquire, Ordering::Acquire) + .is_ok() + { + Some(SpinLockGuard(self)) } else { None } } - pub unsafe fn unsafe_as_ref( &self ) -> &T { + pub unsafe fn unsafe_as_ref(&self) -> &T { &*self.value.get() } - pub unsafe fn force_unlock( &self ) { - self.flag.store( false, Ordering::SeqCst ); + pub unsafe fn force_unlock(&self) { + self.flag.store(false, Ordering::SeqCst); } } -impl< 'a, T > SpinLockGuard< 'a, T > { +impl<'a, T> SpinLockGuard<'a, T> { #[allow(dead_code)] - pub fn unwrap( self ) -> Self { + pub fn unwrap(self) -> Self { self } } -impl< 'a, T > SpinLockGuard< 'a, *mut T > { +impl<'a, T> SpinLockGuard<'a, *mut T> { #[allow(dead_code)] - pub unsafe fn as_ref( self ) -> SpinLockGuard< 'a, &'a T > { - transmute( self ) + pub unsafe fn as_ref(self) -> SpinLockGuard<'a, &'a T> { + transmute(self) } } -impl< 'a, T > Drop for SpinLockGuard< 'a, T > { - fn drop( &mut self ) { - self.0.flag.store( false, Ordering::Release ); +impl<'a, T> Drop for SpinLockGuard<'a, T> { + fn drop(&mut self) { + self.0.flag.store(false, Ordering::Release); } } -impl< 'a, T > Deref for SpinLockGuard< 'a, T > { +impl<'a, T> Deref for SpinLockGuard<'a, T> { type Target = T; - fn deref( &self ) -> &Self::Target { - unsafe { - &*self.0.value.get() - } + fn deref(&self) -> &Self::Target { + unsafe { &*self.0.value.get() } } } -impl< 'a, T > DerefMut for SpinLockGuard< 'a, T > { - fn deref_mut( &mut self ) -> &mut Self::Target { - unsafe { - &mut *self.0.value.get() - } +impl<'a, T> DerefMut for SpinLockGuard<'a, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { &mut *self.0.value.get() } } } diff --git a/preload/src/syscall.rs b/preload/src/syscall.rs index b9692f8d..eb3d2c69 100644 --- a/preload/src/syscall.rs +++ b/preload/src/syscall.rs @@ -1,6 +1,6 @@ -use std::ffi::CStr; -use libc; use crate::utils::Buffer; +use libc; +use std::ffi::CStr; #[cfg(not(feature = "sc"))] macro_rules! syscall { @@ -28,99 +28,119 @@ macro_rules! syscall { }; } -pub fn open( path: &CStr, flags: libc::c_int, mode: libc::c_int ) -> libc::c_int { - open_raw_cstr( path.as_ptr(), flags, mode ) +pub fn open(path: &CStr, flags: libc::c_int, mode: libc::c_int) -> libc::c_int { + open_raw_cstr(path.as_ptr(), flags, mode) } -pub fn open_raw_cstr( path: *const libc::c_char, flags: libc::c_int, mode: libc::c_int ) -> libc::c_int { +pub fn open_raw_cstr( + path: *const libc::c_char, + flags: libc::c_int, + mode: libc::c_int, +) -> libc::c_int { #[cfg(not(target_arch = "aarch64"))] unsafe { - syscall!( OPEN, path, flags, mode ) as _ + syscall!(OPEN, path, flags, mode) as _ } #[cfg(target_arch = "aarch64")] unsafe { - syscall!( OPENAT, libc::AT_FDCWD, path, flags, mode ) as _ + syscall!(OPENAT, libc::AT_FDCWD, path, flags, mode) as _ } } -pub fn close( fd: libc::c_int ) -> libc::c_int { - unsafe { - syscall!( CLOSE, fd ) as _ - } +pub fn close(fd: libc::c_int) -> libc::c_int { + unsafe { syscall!(CLOSE, fd) as _ } } -pub fn write( fd: libc::c_int, buffer: &[u8] ) -> libc::ssize_t { - unsafe { - syscall!( WRITE, fd, buffer.as_ptr(), buffer.len() ) as _ - } +pub fn write(fd: libc::c_int, buffer: &[u8]) -> libc::ssize_t { + unsafe { syscall!(WRITE, fd, buffer.as_ptr(), buffer.len()) as _ } } -pub fn umask( umask: libc::c_int ) -> libc::c_int { - unsafe { - syscall!( UMASK, umask ) as _ - } +pub fn umask(umask: libc::c_int) -> libc::c_int { + unsafe { syscall!(UMASK, umask) as _ } } -pub fn fchmod( fd: libc::c_int, mode: libc::mode_t ) -> libc::c_int { - unsafe { - syscall!( FCHMOD, fd, mode ) as _ - } +pub fn fchmod(fd: libc::c_int, mode: libc::mode_t) -> libc::c_int { + unsafe { syscall!(FCHMOD, fd, mode) as _ } } -pub fn rename( source: &CStr, destination: &CStr ) -> libc::c_int { +pub fn rename(source: &CStr, destination: &CStr) -> libc::c_int { let source = source.as_ptr(); let destination = destination.as_ptr(); #[cfg(not(target_arch = "aarch64"))] unsafe { - syscall!( RENAME, source, destination ) as _ + syscall!(RENAME, source, destination) as _ } #[cfg(target_arch = "aarch64")] unsafe { - syscall!( RENAMEAT, libc::AT_FDCWD, source, libc::AT_FDCWD, destination ) as _ + syscall!( + RENAMEAT, + libc::AT_FDCWD, + source, + libc::AT_FDCWD, + destination + ) as _ } } pub fn getpid() -> libc::pid_t { - unsafe { - syscall!( GETPID ) as libc::pid_t - } + unsafe { syscall!(GETPID) as libc::pid_t } } pub fn gettid() -> u32 { - unsafe { - syscall!( GETTID ) as u32 - } + unsafe { syscall!(GETTID) as u32 } } -pub fn exit( status: u32 ) -> ! { +pub fn exit(status: u32) -> ! { unsafe { - syscall!( EXIT, status ); + syscall!(EXIT, status); core::hint::unreachable_unchecked(); } } #[cfg(target_arch = "arm")] -pub unsafe fn mmap( addr: *mut libc::c_void, length: libc::size_t, prot: libc::c_int, flags: libc::c_int, fildes: libc::c_int, off: libc::off_t ) -> *mut libc::c_void { - syscall!( MMAP2, addr, length, prot, flags, fildes, off / (crate::PAGE_SIZE as libc::off_t) ) as *mut libc::c_void +pub unsafe fn mmap( + addr: *mut libc::c_void, + length: libc::size_t, + prot: libc::c_int, + flags: libc::c_int, + fildes: libc::c_int, + off: libc::off_t, +) -> *mut libc::c_void { + syscall!( + MMAP2, + addr, + length, + prot, + flags, + fildes, + off / (crate::PAGE_SIZE as libc::off_t) + ) as *mut libc::c_void } #[cfg(not(target_arch = "arm"))] -pub unsafe fn mmap( addr: *mut libc::c_void, length: libc::size_t, prot: libc::c_int, flags: libc::c_int, fildes: libc::c_int, off: libc::off_t ) -> *mut libc::c_void { - syscall!( MMAP, addr, length, prot, flags, fildes, off ) as *mut libc::c_void +pub unsafe fn mmap( + addr: *mut libc::c_void, + length: libc::size_t, + prot: libc::c_int, + flags: libc::c_int, + fildes: libc::c_int, + off: libc::off_t, +) -> *mut libc::c_void { + syscall!(MMAP, addr, length, prot, flags, fildes, off) as *mut libc::c_void } -pub unsafe fn munmap( addr: *mut libc::c_void, length: libc::size_t ) -> libc::c_int { - syscall!( MUNMAP, addr, length ) as libc::c_int +pub unsafe fn munmap(addr: *mut libc::c_void, length: libc::size_t) -> libc::c_int { + syscall!(MUNMAP, addr, length) as libc::c_int } extern "C" { static __environ: *const *const u8; } -pub unsafe fn getenv( key: &[u8] ) -> Option< Buffer > { +pub unsafe fn getenv(key: &[u8]) -> Option { let mut current = __environ; loop { let p = *current; @@ -130,39 +150,45 @@ pub unsafe fn getenv( key: &[u8] ) -> Option< Buffer > { let mut r = p; while *r != b'=' { - r = r.add( 1 ); + r = r.add(1); } let entry_key_length = r as usize - p as usize; - let entry_key = std::slice::from_raw_parts( p, entry_key_length ); + let entry_key = std::slice::from_raw_parts(p, entry_key_length); if key == entry_key { - r = r.add( 1 ); + r = r.add(1); let value_pointer = r; while *r != 0 { - r = r.add( 1 ); + r = r.add(1); } let value_length = r as usize - value_pointer as usize; - return Buffer::from_slice( std::slice::from_raw_parts( value_pointer, value_length ) ); + return Buffer::from_slice(std::slice::from_raw_parts(value_pointer, value_length)); } - current = current.add( 1 ); + current = current.add(1); } } #[test] fn test_getenv() { - std::env::set_var( "GETENV_TEST_VAR", "1234" ); - assert_eq!( unsafe { getenv( b"GETENV_TEST_VAR" ) }.unwrap().to_str().unwrap(), "1234" ); + std::env::set_var("GETENV_TEST_VAR", "1234"); + assert_eq!( + unsafe { getenv(b"GETENV_TEST_VAR") } + .unwrap() + .to_str() + .unwrap(), + "1234" + ); } -pub unsafe fn pr_set_vma_anon_name( addr: *mut libc::c_void, length: usize, name: &[u8] ) -> bool { +pub unsafe fn pr_set_vma_anon_name(addr: *mut libc::c_void, length: usize, name: &[u8]) -> bool { let errcode = libc::prctl( libc::PR_SET_VMA, libc::PR_SET_VMA_ANON_NAME, addr, length, - name.as_ptr() + name.as_ptr(), ); errcode >= 0 diff --git a/preload/src/timestamp.rs b/preload/src/timestamp.rs index cef800af..a857f16f 100644 --- a/preload/src/timestamp.rs +++ b/preload/src/timestamp.rs @@ -5,25 +5,25 @@ pub use common::Timestamp; pub fn get_timestamp() -> Timestamp { let mut timespec = libc::timespec { tv_sec: 0, - tv_nsec: 0 + tv_nsec: 0, }; unsafe { - libc::clock_gettime( libc::CLOCK_MONOTONIC, &mut timespec ); + libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut timespec); } - Timestamp::from_timespec( timespec.tv_sec as u64, timespec.tv_nsec as u64 ) + Timestamp::from_timespec(timespec.tv_sec as u64, timespec.tv_nsec as u64) } pub fn get_wall_clock() -> (Timestamp, u64, u64) { let timestamp = get_timestamp(); let mut timespec = libc::timespec { tv_sec: 0, - tv_nsec: 0 + tv_nsec: 0, }; unsafe { - libc::clock_gettime( libc::CLOCK_REALTIME, &mut timespec ); + libc::clock_gettime(libc::CLOCK_REALTIME, &mut timespec); } (timestamp, timespec.tv_sec as u64, timespec.tv_nsec as u64) diff --git a/preload/src/unwind.rs b/preload/src/unwind.rs index 88e31da6..9e842159 100644 --- a/preload/src/unwind.rs +++ b/preload/src/unwind.rs @@ -1,128 +1,143 @@ +use libc::{self, c_int, c_void, uintptr_t}; +use nwind::{LocalAddressSpace, LocalAddressSpaceOptions, LocalUnwindContext, UnwindControl}; +use perf_event_open::{Event, EventSource, Perf}; use std::mem::{self, transmute}; +use std::num::NonZeroUsize; use std::sync::atomic::{AtomicU64, AtomicUsize}; -use libc::{self, c_void, c_int, uintptr_t}; -use perf_event_open::{Perf, EventSource, Event}; -use nwind::{ - LocalAddressSpace, - LocalAddressSpaceOptions, - LocalUnwindContext, - UnwindControl -}; use std::sync::{RwLock, RwLockReadGuard}; use crate::global::StrongThreadHandle; -use crate::spin_lock::SpinLock; -use crate::opt; use crate::nohash::NoHash; +use crate::opt; +use crate::spin_lock::SpinLock; #[repr(C)] pub struct BacktraceHeader { pub key: u64, pub id: AtomicU64, counter: AtomicUsize, - length: usize + length: usize, } -pub struct Backtrace( std::ptr::NonNull< BacktraceHeader > ); +pub struct Backtrace(std::ptr::NonNull); unsafe impl Send for Backtrace {} unsafe impl Sync for Backtrace {} impl Backtrace { - pub fn ptr_eq( lhs: &Backtrace, rhs: &Backtrace ) -> bool { + pub fn ptr_eq(lhs: &Backtrace, rhs: &Backtrace) -> bool { lhs.0.as_ptr() == rhs.0.as_ptr() } - pub fn key( &self ) -> u64 { + pub fn key(&self) -> u64 { self.header().key } - pub fn id( &self ) -> Option< u64 > { - let id = self.header().id.load( std::sync::atomic::Ordering::Relaxed ); + pub fn id(&self) -> Option { + let id = self.header().id.load(std::sync::atomic::Ordering::Relaxed); if id == 0 { None } else { - Some( id ) + Some(id) } } - pub fn set_id( &self, value: u64 ) { - self.header().id.store( value, std::sync::atomic::Ordering::Relaxed ); + pub fn set_id(&self, value: u64) { + self.header() + .id + .store(value, std::sync::atomic::Ordering::Relaxed); } - pub fn frames( &self ) -> &[usize] { + pub fn frames(&self) -> &[usize] { let length = self.header().length; unsafe { - let ptr = (self.0.as_ptr() as *const BacktraceHeader as *const u8).add( std::mem::size_of::< BacktraceHeader >() ) as *const usize; - std::slice::from_raw_parts( ptr, length ) + let ptr = (self.0.as_ptr() as *const BacktraceHeader as *const u8) + .add(std::mem::size_of::()) as *const usize; + std::slice::from_raw_parts(ptr, length) } } } impl Clone for Backtrace { #[inline] - fn clone( &self ) -> Self { - self.header().counter.fetch_add( 1, std::sync::atomic::Ordering::Relaxed ); - Backtrace( self.0.clone() ) + fn clone(&self) -> Self { + self.header() + .counter + .fetch_add(1, std::sync::atomic::Ordering::Relaxed); + Backtrace(self.0.clone()) } } impl Drop for Backtrace { #[inline] - fn drop( &mut self ) { + fn drop(&mut self) { unsafe { - if self.header().counter.fetch_sub( 1, std::sync::atomic::Ordering::Release ) != 1 { + if self + .header() + .counter + .fetch_sub(1, std::sync::atomic::Ordering::Release) + != 1 + { return; } - std::sync::atomic::fence( std::sync::atomic::Ordering::Acquire ); + std::sync::atomic::fence(std::sync::atomic::Ordering::Acquire); self.drop_slow(); } } } impl Backtrace { - fn new( key: u64, backtrace: &[usize] ) -> Self { + fn new(key: u64, backtrace: &[usize]) -> Self { unsafe { let length = backtrace.len(); - let layout = std::alloc::Layout::from_size_align( std::mem::size_of::< BacktraceHeader >() + std::mem::size_of::< usize >() * length, 8 ).unwrap(); - let memory = std::alloc::alloc( layout ) as *mut BacktraceHeader; - std::ptr::write( memory, BacktraceHeader { - key, - id: AtomicU64::new( 0 ), - counter: AtomicUsize::new( 1 ), - length - }); + let layout = std::alloc::Layout::from_size_align( + std::mem::size_of::() + std::mem::size_of::() * length, + 8, + ) + .unwrap(); + let memory = std::alloc::alloc(layout) as *mut BacktraceHeader; + std::ptr::write( + memory, + BacktraceHeader { + key, + id: AtomicU64::new(0), + counter: AtomicUsize::new(1), + length, + }, + ); std::ptr::copy_nonoverlapping( backtrace.as_ptr(), - (memory as *mut u8).add( std::mem::size_of::< BacktraceHeader >() ) as *mut usize, - length + (memory as *mut u8).add(std::mem::size_of::()) as *mut usize, + length, ); - Backtrace( std::ptr::NonNull::new_unchecked( memory ) ) + Backtrace(std::ptr::NonNull::new_unchecked(memory)) } } #[inline(never)] - unsafe fn drop_slow( &mut self ) { + unsafe fn drop_slow(&mut self) { let length = self.header().length; - let layout = std::alloc::Layout::from_size_align( std::mem::size_of::< BacktraceHeader >() + std::mem::size_of::< usize >() * length, 8 ).unwrap(); - std::alloc::dealloc( self.0.as_ptr() as *mut u8, layout ); + let layout = std::alloc::Layout::from_size_align( + std::mem::size_of::() + std::mem::size_of::() * length, + 8, + ) + .unwrap(); + std::alloc::dealloc(self.0.as_ptr() as *mut u8, layout); } #[inline] - fn header( &self ) -> &BacktraceHeader { - unsafe { - self.0.as_ref() - } + fn header(&self) -> &BacktraceHeader { + unsafe { self.0.as_ref() } } } pub struct ThreadUnwindState { unwind_ctx: LocalUnwindContext, last_dl_state: (u64, u64), - current_backtrace: Vec< usize >, - buffer: Vec< usize >, - cache: lru::LruCache< u64, Backtrace, NoHash > + current_backtrace: Vec, + buffer: Vec, + cache: lru::LruCache, } impl ThreadUnwindState { @@ -132,19 +147,28 @@ impl ThreadUnwindState { last_dl_state: (0, 0), current_backtrace: Vec::new(), buffer: Vec::new(), - cache: lru::LruCache::with_hasher( crate::opt::get().backtrace_cache_size_level_1, NoHash ) + cache: lru::LruCache::with_hasher( + NonZeroUsize::new(crate::opt::get().backtrace_cache_size_level_1).unwrap(), + NoHash, + ), } } } type Context = *mut c_void; type ReasonCode = c_int; -type Callback = extern "C" fn( Context, *mut c_void ) -> ReasonCode; +type Callback = extern "C" fn(Context, *mut c_void) -> ReasonCode; extern "C" { - fn _Unwind_Backtrace( callback: Callback, data: *mut c_void ) -> ReasonCode; - fn _Unwind_GetIP( context: Context ) -> uintptr_t; - fn _Unwind_VRS_Get( context: Context, regclass: _Unwind_VRS_RegClass, regno: u32, repr: _Unwind_VRS_DataRepresentation, valuep: *mut c_void ) -> _Unwind_VRS_Result; + fn _Unwind_Backtrace(callback: Callback, data: *mut c_void) -> ReasonCode; + fn _Unwind_GetIP(context: Context) -> uintptr_t; + fn _Unwind_VRS_Get( + context: Context, + regclass: _Unwind_VRS_RegClass, + regno: u32, + repr: _Unwind_VRS_DataRepresentation, + valuep: *mut c_void, + ) -> _Unwind_VRS_Result; } #[allow(non_camel_case_types)] @@ -153,7 +177,7 @@ enum _Unwind_VRS_RegClass { _UVRSC_CORE = 0, _UVRSC_VFP = 1, _UVRSC_WMMXD = 3, - _UVRSC_WMMXC = 4 + _UVRSC_WMMXC = 4, } #[allow(non_camel_case_types)] @@ -163,7 +187,7 @@ enum _Unwind_VRS_DataRepresentation { _UVRSD_VFPX = 1, _UVRSD_UINT64 = 3, _UVRSD_FLOAT = 4, - _UVRSD_DOUBLE = 5 + _UVRSD_DOUBLE = 5, } #[allow(non_camel_case_types)] @@ -171,58 +195,64 @@ enum _Unwind_VRS_DataRepresentation { enum _Unwind_VRS_Result { _UVRSR_OK = 0, _UVRSR_NOT_IMPLEMENTED = 1, - _UVRSR_FAILED = 2 + _UVRSR_FAILED = 2, } #[cfg(not(target_arch = "arm"))] -unsafe fn get_ip( context: Context ) -> uintptr_t { - _Unwind_GetIP( context ) +unsafe fn get_ip(context: Context) -> uintptr_t { + _Unwind_GetIP(context) } #[cfg(target_arch = "arm")] -unsafe fn get_gr( context: Context, index: c_int ) -> uintptr_t { +unsafe fn get_gr(context: Context, index: c_int) -> uintptr_t { let mut value: uintptr_t = 0; - _Unwind_VRS_Get( context, _Unwind_VRS_RegClass::_UVRSC_CORE, index as u32, _Unwind_VRS_DataRepresentation::_UVRSD_UINT32, &mut value as *mut uintptr_t as *mut c_void ); + _Unwind_VRS_Get( + context, + _Unwind_VRS_RegClass::_UVRSC_CORE, + index as u32, + _Unwind_VRS_DataRepresentation::_UVRSD_UINT32, + &mut value as *mut uintptr_t as *mut c_void, + ); value } #[cfg(target_arch = "arm")] -unsafe fn get_ip( context: Context ) -> uintptr_t { - get_gr( context, 15 ) & ( !(0x1 as uintptr_t) ) +unsafe fn get_ip(context: Context) -> uintptr_t { + get_gr(context, 15) & (!(0x1 as uintptr_t)) } -extern "C" fn on_backtrace( context: Context, data: *mut c_void ) -> ReasonCode { +extern "C" fn on_backtrace(context: Context, data: *mut c_void) -> ReasonCode { unsafe { - let out: &mut Vec< usize > = transmute( data ); - out.push( get_ip( context ) as usize ); + let out: &mut Vec = transmute(data); + out.push(get_ip(context) as usize); } 0 } lazy_static! { - static ref AS: RwLock< LocalAddressSpace > = { + static ref AS: RwLock = { let opts = LocalAddressSpaceOptions::new() - .should_load_symbols( cfg!( feature = "debug-logs" ) && log_enabled!( ::log::Level::Debug ) ); + .should_load_symbols(cfg!(feature = "debug-logs") && log_enabled!(::log::Level::Debug)); - let mut address_space = LocalAddressSpace::new_with_opts( opts ).unwrap(); - address_space.use_shadow_stack( opt::get().enable_shadow_stack ); - RwLock::new( address_space ) + let mut address_space = LocalAddressSpace::new_with_opts(opts).unwrap(); + address_space.use_shadow_stack(opt::get().enable_shadow_stack); + RwLock::new(address_space) }; } -pub unsafe fn register_frame_by_pointer( fde: *const u8 ) { - AS.write().unwrap().register_fde_from_pointer( fde ) +pub unsafe fn register_frame_by_pointer(fde: *const u8) { + AS.write().unwrap().register_fde_from_pointer(fde) } -pub fn deregister_frame_by_pointer( fde: *const u8 ) { - AS.write().unwrap().unregister_fde_from_pointer( fde ) +pub fn deregister_frame_by_pointer(fde: *const u8) { + AS.write().unwrap().unregister_fde_from_pointer(fde) } -static mut PERF: Option< SpinLock< Perf > > = None; +static mut PERF: Option> = None; pub fn prepare_to_start_unwinding() { - static FLAG: SpinLock< bool > = SpinLock::new( false ); + static FLAG: SpinLock = SpinLock::new(false); let mut flag = FLAG.lock(); if *flag { return; @@ -239,47 +269,51 @@ pub fn prepare_to_start_unwinding() { let perf = Perf::build() .any_cpu() - .event_source( EventSource::SwDummy ) + .event_source(EventSource::SwDummy) .open(); match perf { - Ok( perf ) => { - unsafe { - PERF = Some( SpinLock::new( perf ) ); - } + Ok(perf) => unsafe { + PERF = Some(SpinLock::new(perf)); }, - Err( error ) => { - warn!( "Failed to initialize perf_event_open: {}", error ); + Err(error) => { + warn!("Failed to initialize perf_event_open: {}", error); } } } fn reload() { let mut address_space = AS.write().unwrap(); - info!( "Reloading address space" ); + info!("Reloading address space"); let timestamp = crate::timestamp::get_timestamp(); let update = address_space.reload().unwrap(); - crate::event::send_event( crate::event::InternalEvent::AddressSpaceUpdated { + crate::event::send_event(crate::event::InternalEvent::AddressSpaceUpdated { timestamp, maps: update.maps, - new_binaries: update.new_binaries + new_binaries: update.new_binaries, }); } -fn reload_if_necessary_perf_event_open( perf: &SpinLock< Perf > ) -> RwLockReadGuard< 'static, LocalAddressSpace > { +fn reload_if_necessary_perf_event_open( + perf: &SpinLock, +) -> RwLockReadGuard<'static, LocalAddressSpace> { if unsafe { perf.unsafe_as_ref().are_events_pending() } { let mut perf = perf.lock(); let mut reload_address_space = false; for event in perf.iter() { match event.get() { - Event::Mmap2( ref event ) if event.filename != b"//anon" && event.inode != 0 && event.protection & libc::PROT_EXEC as u32 != 0 => { - debug!( "New executable region mmaped: {:?}", event ); + Event::Mmap2(ref event) + if event.filename != b"//anon" + && event.inode != 0 + && event.protection & libc::PROT_EXEC as u32 != 0 => + { + debug!("New executable region mmaped: {:?}", event); reload_address_space = true; - }, - Event::Lost( _ ) => { - debug!( "Lost events; forcing a reload" ); + } + Event::Lost(_) => { + debug!("Lost events; forcing a reload"); reload_address_space = true; - }, + } _ => {} } } @@ -292,7 +326,9 @@ fn reload_if_necessary_perf_event_open( perf: &SpinLock< Perf > ) -> RwLockReadG AS.read().unwrap() } -fn reload_if_necessary_dl_iterate_phdr( last_state: &mut (u64, u64) ) -> RwLockReadGuard< 'static, LocalAddressSpace > { +fn reload_if_necessary_dl_iterate_phdr( + last_state: &mut (u64, u64), +) -> RwLockReadGuard<'static, LocalAddressSpace> { let dl_state = get_dl_state(); if *last_state != dl_state { *last_state = dl_state; @@ -303,7 +339,11 @@ fn reload_if_necessary_dl_iterate_phdr( last_state: &mut (u64, u64) ) -> RwLockR } fn get_dl_state() -> (u64, u64) { - unsafe extern fn callback( info: *mut libc::dl_phdr_info, _: libc::size_t, data: *mut libc::c_void ) -> libc::c_int { + unsafe extern "C" fn callback( + info: *mut libc::dl_phdr_info, + _: libc::size_t, + data: *mut libc::c_void, + ) -> libc::c_int { let out = &mut *(data as *mut (u64, u64)); out.0 = (*info).dlpi_adds; out.1 = (*info).dlpi_subs; @@ -312,149 +352,158 @@ fn get_dl_state() -> (u64, u64) { unsafe { let mut out: (u64, u64) = (0, 0); - libc::dl_iterate_phdr( Some( callback ), &mut out as *mut _ as *mut libc::c_void ); + libc::dl_iterate_phdr(Some(callback), &mut out as *mut _ as *mut libc::c_void); out } } #[inline(never)] #[cold] -fn on_broken_unwinding( last_backtrace_depth: usize, stale_frame_count: usize ) { +fn on_broken_unwinding(last_backtrace_depth: usize, stale_frame_count: usize) { error!( "Unwinding is totally broken; last backtrace was {} frames long, and yet we've apparently popped {} frames since last unwind", last_backtrace_depth, stale_frame_count ); - unsafe { libc::abort(); } + unsafe { + libc::abort(); + } } #[inline(always)] -pub fn grab( tls: &mut StrongThreadHandle ) -> Backtrace { +pub fn grab(tls: &mut StrongThreadHandle) -> Backtrace { unsafe { let (is_unwinding, unwind_state) = tls.unwind_state(); *is_unwinding.get() = true; - let backtrace = grab_with_unwind_state( &mut *unwind_state.get() ); + let backtrace = grab_with_unwind_state(&mut *unwind_state.get()); *is_unwinding.get() = false; backtrace } } #[inline(always)] -pub fn grab_from_any( tls: &mut crate::global::ThreadHandleKind ) -> Option< Backtrace > { +pub fn grab_from_any(tls: &mut crate::global::ThreadHandleKind) -> Option { match tls { - crate::global::ThreadHandleKind::Strong( tls ) => { - Some( grab( tls ) ) - }, - crate::global::ThreadHandleKind::Weak( tls ) => { - unsafe { - let (is_unwinding, unwind_state) = tls.unwind_state(); - if *is_unwinding.get() { - return None; - } - - *is_unwinding.get() = true; - let backtrace = grab_with_unwind_state( &mut *unwind_state.get() ); - *is_unwinding.get() = false; - Some( backtrace ) + crate::global::ThreadHandleKind::Strong(tls) => Some(grab(tls)), + crate::global::ThreadHandleKind::Weak(tls) => unsafe { + let (is_unwinding, unwind_state) = tls.unwind_state(); + if *is_unwinding.get() { + return None; } - } + + *is_unwinding.get() = true; + let backtrace = grab_with_unwind_state(&mut *unwind_state.get()); + *is_unwinding.get() = false; + Some(backtrace) + }, } } #[inline(never)] -fn grab_with_unwind_state( unwind_state: &mut ThreadUnwindState ) -> Backtrace { +fn grab_with_unwind_state(unwind_state: &mut ThreadUnwindState) -> Backtrace { let unwind_ctx = &mut unwind_state.unwind_ctx; let address_space = unsafe { - if let Some( ref perf ) = PERF { - reload_if_necessary_perf_event_open( perf ) + if let Some(ref perf) = PERF { + reload_if_necessary_perf_event_open(perf) } else { - reload_if_necessary_dl_iterate_phdr( &mut unwind_state.last_dl_state ) + reload_if_necessary_dl_iterate_phdr(&mut unwind_state.last_dl_state) } }; let stale_count; - let debug_crosscheck_unwind_results = opt::crosscheck_unwind_results_with_libunwind() && !address_space.is_shadow_stack_enabled(); + let debug_crosscheck_unwind_results = + opt::crosscheck_unwind_results_with_libunwind() && !address_space.is_shadow_stack_enabled(); if debug_crosscheck_unwind_results || !opt::emit_partial_backtraces() { stale_count = unwind_state.current_backtrace.len(); let buffer = &mut unwind_state.buffer; buffer.clear(); - address_space.unwind( unwind_ctx, |address| { - buffer.push( address ); + address_space.unwind(unwind_ctx, |address| { + buffer.push(address); UnwindControl::Continue }); } else { let buffer = &mut unwind_state.buffer; buffer.clear(); - let stale_count_opt = address_space.unwind_through_fresh_frames( unwind_ctx, |address| { - buffer.push( address ); + let stale_count_opt = address_space.unwind_through_fresh_frames(unwind_ctx, |address| { + buffer.push(address); UnwindControl::Continue }); let last_backtrace_depth = unwind_state.current_backtrace.len(); let mut new_backtrace_depth = buffer.len(); - if let Some( stale_frame_count ) = stale_count_opt { + if let Some(stale_frame_count) = stale_count_opt { if stale_frame_count > last_backtrace_depth { - on_broken_unwinding( last_backtrace_depth, stale_frame_count ); + on_broken_unwinding(last_backtrace_depth, stale_frame_count); } else { new_backtrace_depth += last_backtrace_depth - stale_frame_count; - if cfg!( feature = "debug-logs" ) { - debug!( "Finished unwinding; backtrace depth: {} (fresh = {}, non-fresh = {})", new_backtrace_depth, buffer.len(), last_backtrace_depth - stale_frame_count ); + if cfg!(feature = "debug-logs") { + debug!( + "Finished unwinding; backtrace depth: {} (fresh = {}, non-fresh = {})", + new_backtrace_depth, + buffer.len(), + last_backtrace_depth - stale_frame_count + ); } } } else { - if cfg!( feature = "debug-logs" ) { - debug!( "Finished unwinding; backtrace depth: {}", new_backtrace_depth ); + if cfg!(feature = "debug-logs") { + debug!( + "Finished unwinding; backtrace depth: {}", + new_backtrace_depth + ); } } - stale_count = stale_count_opt.unwrap_or( unwind_state.current_backtrace.len() ); + stale_count = stale_count_opt.unwrap_or(unwind_state.current_backtrace.len()); } - mem::drop( address_space ); + mem::drop(address_space); let remaining = unwind_state.current_backtrace.len() - stale_count; - unwind_state.current_backtrace.truncate( remaining ); - unwind_state.current_backtrace.reserve( unwind_state.buffer.len() ); + unwind_state.current_backtrace.truncate(remaining); + unwind_state + .current_backtrace + .reserve(unwind_state.buffer.len()); const PRIME: u64 = 1099511628211; let mut key: u64 = 0; for &frame in &unwind_state.current_backtrace { - key = key.wrapping_mul( PRIME ); + key = key.wrapping_mul(PRIME); key ^= frame as u64; } for &frame in unwind_state.buffer.iter().rev() { - key = key.wrapping_mul( PRIME ); + key = key.wrapping_mul(PRIME); key ^= frame as u64; - unwind_state.current_backtrace.push( frame ); + unwind_state.current_backtrace.push(frame); } unwind_state.buffer.clear(); - let backtrace = match unwind_state.cache.get_mut( &key ) { + let backtrace = match unwind_state.cache.get_mut(&key) { None => { - if cfg!( debug_assertions ) { - if unwind_state.cache.len() >= unwind_state.cache.cap() { - debug!( "1st level backtrace cache overflow" ); + if cfg!(debug_assertions) { + if unwind_state.cache.len() >= unwind_state.cache.cap().get() { + debug!("1st level backtrace cache overflow"); } } - let entry = Backtrace::new( key, &unwind_state.current_backtrace ); - unwind_state.cache.put( key, entry.clone() ); + let entry = Backtrace::new(key, &unwind_state.current_backtrace); + unwind_state.cache.put(key, entry.clone()); entry - }, - Some( entry ) => { + } + Some(entry) => { if entry.frames() == unwind_state.current_backtrace { entry.clone() } else { - info!( "1st level backtrace cache conflict detected!" ); + info!("1st level backtrace cache conflict detected!"); - let new_entry = Backtrace::new( key, &unwind_state.current_backtrace ); + let new_entry = Backtrace::new(key, &unwind_state.current_backtrace); *entry = new_entry.clone(); new_entry @@ -463,31 +512,46 @@ fn grab_with_unwind_state( unwind_state: &mut ThreadUnwindState ) -> Backtrace { }; if debug_crosscheck_unwind_results { - let mut expected: Vec< usize > = Vec::with_capacity( backtrace.frames().len() ); + let mut expected: Vec = Vec::with_capacity(backtrace.frames().len()); unsafe { - _Unwind_Backtrace( on_backtrace, transmute( &mut expected ) ); + _Unwind_Backtrace(on_backtrace, transmute(&mut expected)); } - if expected.last() == Some( &0 ) { + if expected.last() == Some(&0) { expected.pop(); } expected.reverse(); - if backtrace.frames()[ ..backtrace.frames().len() - 1 ] != expected[ ..expected.len() - 1 ] { - info!( "/proc/self/maps:\n{}", String::from_utf8_lossy( &::std::fs::read( "/proc/self/maps" ).unwrap() ).trim() ); + if backtrace.frames()[..backtrace.frames().len() - 1] != expected[..expected.len() - 1] { + info!( + "/proc/self/maps:\n{}", + String::from_utf8_lossy(&::std::fs::read("/proc/self/maps").unwrap()).trim() + ); let address_space = AS.read().unwrap(); - info!( "Expected: ({} frames)", expected.len() ); + info!("Expected: ({} frames)", expected.len()); for (nth, &address) in expected.iter().enumerate() { - info!( "({:02}) {:?}", nth, address_space.decode_symbol_once( address ) ); + info!( + "({:02}) {:?}", + nth, + address_space.decode_symbol_once(address) + ); } - info!( "Actual: ({} frames)", backtrace.frames().len() ); + info!("Actual: ({} frames)", backtrace.frames().len()); for (nth, &address) in backtrace.frames().iter().enumerate() { - info!( "({:02}) {:?}", nth, address_space.decode_symbol_once( address ) ); + info!( + "({:02}) {:?}", + nth, + address_space.decode_symbol_once(address) + ); } - panic!( "Wrong backtrace; expected: {:?}, got: {:?}", expected, backtrace.frames() ); + panic!( + "Wrong backtrace; expected: {:?}, got: {:?}", + expected, + backtrace.frames() + ); } } diff --git a/preload/src/utils.rs b/preload/src/utils.rs index aaeb6b8f..b7abf487 100644 --- a/preload/src/utils.rs +++ b/preload/src/utils.rs @@ -1,202 +1,224 @@ -use std::io::{self, Read, Write}; -use std::fs::File; +use std::ffi::OsStr; use std::fmt; +use std::fs::File; +use std::io::{self, Read, Write}; use std::mem::MaybeUninit; +use std::os::unix::ffi::OsStrExt; +use std::path::Path; use std::ptr; use std::sync::atomic::{AtomicUsize, Ordering}; -use std::ffi::OsStr; -use std::path::Path; -use std::os::unix::ffi::OsStrExt; -use crate::{EXECUTABLE, PID}; use crate::syscall; +use crate::{EXECUTABLE, PID}; -pub fn read_file( path: &str ) -> io::Result< Vec< u8 > > { - let mut fp = File::open( path )?; +pub fn read_file(path: &str) -> io::Result> { + let mut fp = File::open(path)?; let mut buffer = Vec::new(); - fp.read_to_end( &mut buffer )?; - Ok( buffer ) + fp.read_to_end(&mut buffer)?; + Ok(buffer) } -pub fn copy< I: Read, O: Write >( mut input: I, mut output: O ) -> io::Result< () > { +pub fn copy(mut input: I, mut output: O) -> io::Result<()> { let mut buffer = [0; 64 * 1024]; loop { - let count = input.read( &mut buffer )?; + let count = input.read(&mut buffer)?; if count == 0 { break; } - output.write_all( &buffer[ 0..count ] )?; + output.write_all(&buffer[0..count])?; } Ok(()) } -pub struct RestoreFileCreationMaskOnDrop( libc::c_int ); +pub struct RestoreFileCreationMaskOnDrop(libc::c_int); impl Drop for RestoreFileCreationMaskOnDrop { - fn drop( &mut self ) { - syscall::umask( self.0 ); + fn drop(&mut self) { + syscall::umask(self.0); } } -pub fn temporarily_change_umask( umask: libc::c_int ) -> RestoreFileCreationMaskOnDrop { - let old_umask = syscall::umask( umask ); - RestoreFileCreationMaskOnDrop( old_umask ) +pub fn temporarily_change_umask(umask: libc::c_int) -> RestoreFileCreationMaskOnDrop { + let old_umask = syscall::umask(umask); + RestoreFileCreationMaskOnDrop(old_umask) } const STACK_BUFFER_DEFAULT_LEN: usize = 1024; -pub struct Buffer< const L: usize = STACK_BUFFER_DEFAULT_LEN > { - buffer: [MaybeUninit< u8 >; L], - length: usize +pub struct Buffer { + buffer: [MaybeUninit; L], + length: usize, } -impl< const L: usize > std::fmt::Debug for Buffer< L > { - fn fmt( &self, formatter: &mut std::fmt::Formatter ) -> std::fmt::Result { +impl std::fmt::Debug for Buffer { + fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { let slice = self.as_slice(); - if let Ok( string ) = std::str::from_utf8( slice ) { - formatter.write_str( string ) + if let Ok(string) = std::str::from_utf8(slice) { + formatter.write_str(string) } else { - self.buffer[ 0..self.length ].fmt( formatter ) + self.buffer[0..self.length].fmt(formatter) } } } -impl< const L: usize > Buffer< L > { +impl Buffer { pub const fn new() -> Self { unsafe { Self { - buffer: MaybeUninit::< [MaybeUninit< u8 >; L] >::uninit().assume_init(), - length: 0 + buffer: MaybeUninit::<[MaybeUninit; L]>::uninit().assume_init(), + length: 0, } } } - pub const fn from_fixed_slice< const N: usize >( slice: &[u8; N] ) -> Self { + pub const fn from_fixed_slice(slice: &[u8; N]) -> Self { let mut buffer = Self::new(); let mut nth = 0; while nth < N { - buffer.buffer[ nth ] = MaybeUninit::new( slice[ nth ] ); + buffer.buffer[nth] = MaybeUninit::new(slice[nth]); nth += 1; } buffer.length = N; buffer } - pub fn from_slice( slice: &[u8] ) -> Option< Self > { + pub fn from_slice(slice: &[u8]) -> Option { if slice.len() > L { return None; } let mut buffer = Self::new(); - buffer.write( slice ).unwrap(); - Some( buffer ) + buffer.write(slice).unwrap(); + Some(buffer) } - pub fn to_str( &self ) -> Option< &str > { - std::str::from_utf8( self.as_slice() ).ok() + pub fn to_str(&self) -> Option<&str> { + std::str::from_utf8(self.as_slice()).ok() } - pub fn as_slice( &self ) -> &[u8] { - unsafe { std::slice::from_raw_parts( self.buffer.as_ptr() as *const u8, self.length ) } + pub fn as_slice(&self) -> &[u8] { + unsafe { std::slice::from_raw_parts(self.buffer.as_ptr() as *const u8, self.length) } } - fn as_slice_mut( &mut self ) -> &mut [u8] { - unsafe { std::slice::from_raw_parts_mut( self.buffer.as_mut_ptr() as *mut u8, self.length ) } + fn as_slice_mut(&mut self) -> &mut [u8] { + unsafe { std::slice::from_raw_parts_mut(self.buffer.as_mut_ptr() as *mut u8, self.length) } } - fn push( &mut self, byte: u8 ) { + fn push(&mut self, byte: u8) { if self.length >= self.buffer.len() { return; } - self.buffer[ self.length ] = MaybeUninit::new( byte ); + self.buffer[self.length] = MaybeUninit::new(byte); self.length += 1; } } -impl< const L: usize > std::ops::Deref for Buffer< L > { +impl std::ops::Deref for Buffer { type Target = [u8]; - fn deref( &self ) -> &Self::Target { + fn deref(&self) -> &Self::Target { self.as_slice() } } -impl< const L: usize > AsRef< OsStr > for Buffer< L > { - fn as_ref( &self ) -> &OsStr { - OsStr::from_bytes( self.as_slice() ) +impl AsRef for Buffer { + fn as_ref(&self) -> &OsStr { + OsStr::from_bytes(self.as_slice()) } } -impl< const L: usize > AsRef< Path > for Buffer< L > { - fn as_ref( &self ) -> &Path { - Path::new( self ) +impl AsRef for Buffer { + fn as_ref(&self) -> &Path { + Path::new(self) } } -impl< const L: usize > Write for Buffer< L > { - fn write( &mut self, input: &[u8] ) -> io::Result< usize > { - let count = std::cmp::min( input.len(), L - self.length ); +impl Write for Buffer { + fn write(&mut self, input: &[u8]) -> io::Result { + let count = std::cmp::min(input.len(), L - self.length); unsafe { - std::ptr::copy_nonoverlapping( input.as_ptr(), self.buffer[ self.length.. ].as_mut_ptr() as *mut u8, count ); + std::ptr::copy_nonoverlapping( + input.as_ptr(), + self.buffer[self.length..].as_mut_ptr() as *mut u8, + count, + ); } self.length += count; - Ok( count ) + Ok(count) } - fn flush( &mut self ) -> io::Result< () > { + fn flush(&mut self) -> io::Result<()> { Ok(()) } } -fn stack_format< const L: usize, R, F, G >( format_callback: F, use_callback: G ) -> R - where F: FnOnce( &mut Buffer< L > ), - G: FnOnce( &mut [u8] ) -> R +fn stack_format(format_callback: F, use_callback: G) -> R +where + F: FnOnce(&mut Buffer), + G: FnOnce(&mut [u8]) -> R, { let mut buffer = Buffer::new(); - format_callback( &mut buffer ); - use_callback( buffer.as_slice_mut() ) + format_callback(&mut buffer); + use_callback(buffer.as_slice_mut()) } #[test] fn test_stack_format_short() { - stack_format( |out: &mut Buffer< STACK_BUFFER_DEFAULT_LEN >| { - write!( out, "foo = {}", "bar" ).unwrap(); - write!( out, ";" ).unwrap(); - }, |output| { - assert_eq!( output, b"foo = bar;" ); - }); + stack_format( + |out: &mut Buffer| { + write!(out, "foo = {}", "bar").unwrap(); + write!(out, ";").unwrap(); + }, + |output| { + assert_eq!(output, b"foo = bar;"); + }, + ); } #[test] fn test_stack_format_long() { - stack_format( |out: &mut Buffer< STACK_BUFFER_DEFAULT_LEN >| { - for _ in 0..STACK_BUFFER_DEFAULT_LEN { - write!( out, "X" ).unwrap(); - } - assert!( write!( out, "Y" ).is_err() ); - }, |output| { - assert_eq!( output.len(), STACK_BUFFER_DEFAULT_LEN ); - assert!( output.iter().all( |&byte| byte == b'X' ) ); - }); + stack_format( + |out: &mut Buffer| { + for _ in 0..STACK_BUFFER_DEFAULT_LEN { + write!(out, "X").unwrap(); + } + assert!(write!(out, "Y").is_err()); + }, + |output| { + assert_eq!(output.len(), STACK_BUFFER_DEFAULT_LEN); + assert!(output.iter().all(|&byte| byte == b'X')); + }, + ); } -pub fn stack_format_bytes< R, F >( args: fmt::Arguments, callback: F ) -> R - where F: FnOnce( &mut [u8] ) -> R +pub fn stack_format_bytes(args: fmt::Arguments, callback: F) -> R +where + F: FnOnce(&mut [u8]) -> R, { - stack_format( |out: &mut Buffer< STACK_BUFFER_DEFAULT_LEN >| { - let _ = out.write_fmt( args ); - }, callback ) + stack_format( + |out: &mut Buffer| { + let _ = out.write_fmt(args); + }, + callback, + ) } -pub fn stack_null_terminate< R, F >( input: &[u8], callback: F ) -> R - where F: FnOnce( &mut [u8] ) -> R +pub fn stack_null_terminate(input: &[u8], callback: F) -> R +where + F: FnOnce(&mut [u8]) -> R, { - stack_format( |out: &mut Buffer< STACK_BUFFER_DEFAULT_LEN >| { - let _ = out.write_all( input ); - let _ = out.write_all( &[0] ); - }, callback ) + stack_format( + |out: &mut Buffer| { + let _ = out.write_all(input); + let _ = out.write_all(&[0]); + }, + callback, + ) } -pub fn generate_filename( pattern: &[u8], counter: Option< &AtomicUsize > ) -> Buffer< STACK_BUFFER_DEFAULT_LEN > { +pub fn generate_filename( + pattern: &[u8], + counter: Option<&AtomicUsize>, +) -> Buffer { let mut output = Buffer::new(); let mut seen_percent = false; for &ch in pattern.as_ref() { @@ -209,31 +231,32 @@ pub fn generate_filename( pattern: &[u8], counter: Option< &AtomicUsize > ) -> B seen_percent = false; match ch { b'%' => { - output.push( ch ); - }, + output.push(ch); + } b'p' => { let pid = *PID; - write!( &mut output, "{}", pid ).unwrap(); - }, + write!(&mut output, "{}", pid).unwrap(); + } b't' => { - let timestamp = unsafe { libc::time( ptr::null_mut() ) }; - write!( &mut output, "{}", timestamp ).unwrap(); - }, + let timestamp = unsafe { libc::time(ptr::null_mut()) }; + write!(&mut output, "{}", timestamp).unwrap(); + } b'e' => { - let executable = String::from_utf8_lossy( &*EXECUTABLE ); - let executable = &executable[ executable.rfind( "/" ).map( |index| index + 1 ).unwrap_or( 0 ).. ]; - write!( &mut output, "{}", executable ).unwrap(); - }, + let executable = String::from_utf8_lossy(&*EXECUTABLE); + let executable = + &executable[executable.rfind("/").map(|index| index + 1).unwrap_or(0)..]; + write!(&mut output, "{}", executable).unwrap(); + } b'n' => { - if let Some( counter ) = counter { - let value = counter.fetch_add( 1, Ordering::SeqCst ); - write!( &mut output, "{}", value ).unwrap(); + if let Some(counter) = counter { + let value = counter.fetch_add(1, Ordering::SeqCst); + write!(&mut output, "{}", value).unwrap(); } - }, + } _ => {} } } else { - output.push( ch ); + output.push(ch); } } @@ -241,30 +264,32 @@ pub fn generate_filename( pattern: &[u8], counter: Option< &AtomicUsize > ) -> B } #[repr(align(64))] -pub struct CacheAligned< T >( pub T ); +pub struct CacheAligned(pub T); -impl< T > std::ops::Deref for CacheAligned< T > { +impl std::ops::Deref for CacheAligned { type Target = T; #[inline(always)] - fn deref( &self ) -> &Self::Target { + fn deref(&self) -> &Self::Target { &self.0 } } -impl< T > std::ops::DerefMut for CacheAligned< T > { +impl std::ops::DerefMut for CacheAligned { #[inline(always)] - fn deref_mut( &mut self ) -> &mut Self::Target { + fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } -pub type HashMap< K, V > = hashbrown::HashMap< K, V, ahash::random_state::RandomState >; -pub type HashSet< T > = hashbrown::HashSet< T, ahash::random_state::RandomState >; -pub type Entry< 'a, K, V > = hashbrown::hash_map::Entry< 'a, K, V, ahash::random_state::RandomState >; -pub const fn empty_hashmap< K, V >() -> HashMap< K, V > { - hashbrown::HashMap::with_hasher( ahash::random_state::RandomState::with_seeds( - 0x40b1d1a46e72d5af, 0xfa484741bb13cbac, - 0x059a48d9d09ed59d, 0x08ab62f8e225add9 +pub type HashMap = hashbrown::HashMap; +pub type HashSet = hashbrown::HashSet; +pub type Entry<'a, K, V> = hashbrown::hash_map::Entry<'a, K, V, ahash::random_state::RandomState>; +pub const fn empty_hashmap() -> HashMap { + hashbrown::HashMap::with_hasher(ahash::random_state::RandomState::with_seeds( + 0x40b1d1a46e72d5af, + 0xfa484741bb13cbac, + 0x059a48d9d09ed59d, + 0x08ab62f8e225add9, )) } diff --git a/preload/src/writer_memory.rs b/preload/src/writer_memory.rs index 9c400d6d..23c7968f 100644 --- a/preload/src/writer_memory.rs +++ b/preload/src/writer_memory.rs @@ -1,6 +1,6 @@ use std::cmp::min; use std::fs::File; -use std::io::{self, Read, Write, Seek, SeekFrom}; +use std::io::{self, Read, Seek, SeekFrom, Write}; use std::ptr; use nwind::proc_maps::parse as parse_maps; @@ -8,33 +8,32 @@ use nwind::proc_maps::parse as parse_maps; use common::event::Event; use common::speedy::Writable; -use crate::PAGE_SIZE; use crate::syscall; use crate::writers::write_maps; +use crate::PAGE_SIZE; -fn is_accessible< U: Read + Seek >( mut fp: U, address: u64 ) -> bool { - if let Err( _ ) = fp.seek( SeekFrom::Start( address ) ) { +fn is_accessible(mut fp: U, address: u64) -> bool { + if let Err(_) = fp.seek(SeekFrom::Start(address)) { return false; } let mut dummy: [u8; 1] = [0]; - match fp.read( &mut dummy ) { - Ok( 1 ) => true, - _ => false + match fp.read(&mut dummy) { + Ok(1) => true, + _ => false, } } -fn memory_dump_body< U: Write >( mut serializer: &mut U ) -> io::Result< () > { +fn memory_dump_body(mut serializer: &mut U) -> io::Result<()> { let mut buffer = Vec::new(); - buffer.resize( 1024 * 128, 0 ); + buffer.resize(1024 * 128, 0); let mut buffer = buffer.into_boxed_slice(); - let maps = write_maps( serializer )?; - let maps = String::from_utf8_lossy( &maps ); - let maps = parse_maps( &maps ); - let mut fp = File::open( "/proc/self/mem" )?; + let maps = write_maps(serializer)?; + let maps = String::from_utf8_lossy(&maps); + let maps = parse_maps(&maps); + let mut fp = File::open("/proc/self/mem")?; let page_size = PAGE_SIZE as u64; - for region in maps { if !region.is_write && region.inode != 0 { continue; @@ -51,7 +50,8 @@ fn memory_dump_body< U: Write >( mut serializer: &mut U ) -> io::Result< () > { } let current = start + (end - start) / 2; - let accessible = is_accessible( &mut fp, region.start + current * page_size + page_size - 1 ); + let accessible = + is_accessible(&mut fp, region.start + current * page_size + page_size - 1); if !accessible { end = current; } else { @@ -63,73 +63,74 @@ fn memory_dump_body< U: Write >( mut serializer: &mut U ) -> io::Result< () > { }; loop { - let chunk_size = min( buffer.len() as u64, end - region.start ); + let chunk_size = min(buffer.len() as u64, end - region.start); if chunk_size == 0 { break; } let address = end - chunk_size; - fp.seek( SeekFrom::Start( address ) )?; - fp.read_exact( &mut buffer[ 0..chunk_size as usize ] )?; - let data = &buffer[ 0..chunk_size as usize ]; + fp.seek(SeekFrom::Start(address))?; + fp.read_exact(&mut buffer[0..chunk_size as usize])?; + let data = &buffer[0..chunk_size as usize]; Event::MemoryDump { address, length: chunk_size as u64, - data: data.into() - }.write_to_stream( &mut serializer )?; + data: data.into(), + } + .write_to_stream(&mut serializer)?; end -= chunk_size; } -/* - let mut page: [u8; 4096] = [0; 4096]; - while address > region.start { - fp.seek( SeekFrom::Start( address - page_size ) )?; - fp.read_exact( &mut page )?; - - if page.iter().all( |&byte| byte == 0 ) { - address -= page_size; - } else { - break; - } - } - - fp.seek( SeekFrom::Start( region.start ) )?; - let mut current = region.start; + /* + let mut page: [u8; 4096] = [0; 4096]; + while address > region.start { + fp.seek( SeekFrom::Start( address - page_size ) )?; + fp.read_exact( &mut page )?; + + if page.iter().all( |&byte| byte == 0 ) { + address -= page_size; + } else { + break; + } + } - while current < address { - let chunk_size = min( buffer.len(), (address - current) as usize ); - fp.read_exact( &mut buffer[ 0..chunk_size ] )?; - let data = &buffer[ 0..chunk_size ]; - Event::MemoryDump { - address: current, - length: chunk_size as u64, - data - }.write_to_stream( LittleEndian, serializer )?; - current += chunk_size as u64; - } -*/ + fp.seek( SeekFrom::Start( region.start ) )?; + let mut current = region.start; + + while current < address { + let chunk_size = min( buffer.len(), (address - current) as usize ); + fp.read_exact( &mut buffer[ 0..chunk_size ] )?; + let data = &buffer[ 0..chunk_size ]; + Event::MemoryDump { + address: current, + length: chunk_size as u64, + data + }.write_to_stream( LittleEndian, serializer )?; + current += chunk_size as u64; + } + */ } serializer.flush()?; Ok(()) } -pub fn write_memory_dump< U: Write >( serializer: &mut U ) -> io::Result< () > { - info!( "Writing a memory dump..." ); +pub fn write_memory_dump(serializer: &mut U) -> io::Result<()> { + info!("Writing a memory dump..."); serializer.flush()?; let pid = unsafe { libc::fork() }; if pid == 0 { - let result = memory_dump_body( serializer ); - syscall::exit( if result.is_err() { 1 } else { 0 } ); + let result = memory_dump_body(serializer); + syscall::exit(if result.is_err() { 1 } else { 0 }); } else { - info!( "Waiting for child to finish..." ); + info!("Waiting for child to finish..."); unsafe { - libc::waitpid( pid, ptr::null_mut(), 0 ); + libc::waitpid(pid, ptr::null_mut(), 0); } } - info!( "Memory dump finished" ); + info!("Memory dump finished"); Ok(()) } diff --git a/preload/src/writers.rs b/preload/src/writers.rs index c1fd8874..88deca59 100644 --- a/preload/src/writers.rs +++ b/preload/src/writers.rs @@ -5,64 +5,74 @@ use std::io::{self, Write}; use std::mem; use std::path::Path; -use nwind::proc_maps::Region; use nwind::proc_maps::parse as parse_maps; +use nwind::proc_maps::Region; use common::event::{DataId, Event, HeaderBody, HEADER_FLAG_IS_LITTLE_ENDIAN}; use common::speedy::Writable; use common::Timestamp; -use crate::{CMDLINE, EXECUTABLE, PID}; use crate::arch; use crate::opt; -use crate::timestamp::{get_timestamp, get_wall_clock}; -use crate::utils::read_file; use crate::processing_thread::BacktraceCache; +use crate::timestamp::{get_timestamp, get_wall_clock}; use crate::unwind::Backtrace; +use crate::utils::read_file; +use crate::{CMDLINE, EXECUTABLE, PID}; -fn read_maps() -> io::Result< Vec< Region > > { - let maps = read_file( "/proc/self/maps" )?; - let maps_str = String::from_utf8_lossy( &maps ); - let regions = parse_maps( &maps_str ); - Ok( regions ) +fn read_maps() -> io::Result> { + let maps = read_file("/proc/self/maps")?; + let maps_str = String::from_utf8_lossy(&maps); + let regions = parse_maps(&maps_str); + Ok(regions) } -fn mmap_file< P: AsRef< Path >, R, F: FnOnce( &[u8] ) -> R >( path: P, callback: F ) -> io::Result< R > { +fn mmap_file, R, F: FnOnce(&[u8]) -> R>(path: P, callback: F) -> io::Result { use std::os::fd::AsRawFd; - let fp = File::open( &path )?; + let fp = File::open(&path)?; let length = fp.metadata()?.len(); let pointer = unsafe { - crate::syscall::mmap( std::ptr::null_mut(), length as libc::size_t, libc::PROT_READ, libc::MAP_PRIVATE, fp.as_raw_fd(), 0 ) + crate::syscall::mmap( + std::ptr::null_mut(), + length as libc::size_t, + libc::PROT_READ, + libc::MAP_PRIVATE, + fp.as_raw_fd(), + 0, + ) }; if pointer == libc::MAP_FAILED { - return Err( std::io::Error::last_os_error() ); + return Err(std::io::Error::last_os_error()); } - struct UnmapOnDrop( *mut libc::c_void, u64 ); + struct UnmapOnDrop(*mut libc::c_void, u64); impl Drop for UnmapOnDrop { - fn drop( &mut self ) { - if unsafe { crate::syscall::munmap( self.0, self.1 as libc::size_t ) } < 0 { - error!( "munmap failed" ); + fn drop(&mut self) { + if unsafe { crate::syscall::munmap(self.0, self.1 as libc::size_t) } < 0 { + error!("munmap failed"); } } } - let _unmap_on_drop = UnmapOnDrop( pointer, length ); - Ok( callback( unsafe { std::slice::from_raw_parts( pointer.cast::< u8 >(), length as usize ) } ) ) + let _unmap_on_drop = UnmapOnDrop(pointer, length); + Ok(callback(unsafe { + std::slice::from_raw_parts(pointer.cast::(), length as usize) + })) } -fn write_file< U: Write >( mut serializer: &mut U, path: &str, bytes: &[u8] ) -> io::Result< () > { +fn write_file(mut serializer: &mut U, path: &str, bytes: &[u8]) -> io::Result<()> { Event::File64 { timestamp: get_timestamp(), path: path.into(), - contents: bytes.into() - }.write_to_stream( &mut serializer )?; + contents: bytes.into(), + } + .write_to_stream(&mut serializer)?; Ok(()) } -fn new_header_body( id: DataId, initial_timestamp: Timestamp ) -> io::Result< HeaderBody > { +fn new_header_body(id: DataId, initial_timestamp: Timestamp) -> io::Result { let (timestamp, wall_clock_secs, wall_clock_nsecs) = get_wall_clock(); let mut flags = 0; @@ -70,7 +80,7 @@ fn new_header_body( id: DataId, initial_timestamp: Timestamp ) -> io::Result< He flags |= HEADER_FLAG_IS_LITTLE_ENDIAN; } - Ok( HeaderBody { + Ok(HeaderBody { id, initial_timestamp, timestamp, @@ -81,16 +91,20 @@ fn new_header_body( id: DataId, initial_timestamp: Timestamp ) -> io::Result< He executable: EXECUTABLE.clone(), arch: arch::TARGET_ARCH.to_string(), flags, - pointer_size: mem::size_of::< usize >() as u8 + pointer_size: mem::size_of::() as u8, }) } -pub fn write_header< U: Write >( id: DataId, initial_timestamp: Timestamp, serializer: &mut U ) -> io::Result< () > { - Event::Header( new_header_body( id, initial_timestamp )? ).write_to_stream( serializer )?; +pub fn write_header( + id: DataId, + initial_timestamp: Timestamp, + serializer: &mut U, +) -> io::Result<()> { + Event::Header(new_header_body(id, initial_timestamp)?).write_to_stream(serializer)?; Ok(()) } -pub fn write_binaries< U: Write >( mut serializer: &mut U ) -> io::Result< () > { +pub fn write_binaries(mut serializer: &mut U) -> io::Result<()> { let regions = read_maps()?; let mut files = HashSet::new(); for region in regions { @@ -98,26 +112,30 @@ pub fn write_binaries< U: Write >( mut serializer: &mut U ) -> io::Result< () > continue; } - if region.name == "[heap]" || region.name == "[stack]" || region.name == "[vdso]" || region.name == "[vsyscall]" { + if region.name == "[heap]" + || region.name == "[stack]" + || region.name == "[vdso]" + || region.name == "[vsyscall]" + { continue; } - if files.contains( ®ion.name ) { + if files.contains(®ion.name) { continue; } - files.insert( region.name ); + files.insert(region.name); } serializer.flush()?; for filename in files { - debug!( "Writing '{}'...", filename ); - match mmap_file( &filename, |bytes| write_file( &mut serializer, &filename, bytes ) ) { - Ok( result ) => { - result? - }, - Err( error ) => { - debug!( "Failed to mmap '{}': {}", filename, error ); + debug!("Writing '{}'...", filename); + match mmap_file(&filename, |bytes| { + write_file(&mut serializer, &filename, bytes) + }) { + Ok(result) => result?, + Err(error) => { + debug!("Failed to mmap '{}': {}", filename, error); } } } @@ -125,24 +143,34 @@ pub fn write_binaries< U: Write >( mut serializer: &mut U ) -> io::Result< () > Ok(()) } -pub fn write_maps< U: Write >( serializer: &mut U ) -> io::Result< Vec< u8 > > { - let maps = read_file( "/proc/self/maps" )?; - Event::File64 { timestamp: get_timestamp(), path: "/proc/self/maps".into(), contents: maps.clone().into() }.write_to_stream( serializer )?; - Ok( maps ) +pub fn write_maps(serializer: &mut U) -> io::Result> { + let maps = read_file("/proc/self/maps")?; + Event::File64 { + timestamp: get_timestamp(), + path: "/proc/self/maps".into(), + contents: maps.clone().into(), + } + .write_to_stream(serializer)?; + Ok(maps) } -fn write_wallclock< U: Write >( serializer: &mut U ) -> io::Result< () > { +fn write_wallclock(serializer: &mut U) -> io::Result<()> { let (timestamp, sec, nsec) = get_wall_clock(); - Event::WallClock { timestamp, sec, nsec }.write_to_stream( serializer )?; + Event::WallClock { + timestamp, + sec, + nsec, + } + .write_to_stream(serializer)?; Ok(()) } -fn write_uptime< U: Write >( serializer: &mut U ) -> io::Result< () > { - let uptime = fs::read( "/proc/uptime" )?; - write_file( serializer, "/proc/uptime", &uptime ) +fn write_uptime(serializer: &mut U) -> io::Result<()> { + let uptime = fs::read("/proc/uptime")?; + write_file(serializer, "/proc/uptime", &uptime) } -fn write_environ< U: Write >( mut serializer: U ) -> io::Result< () > { +fn write_environ(mut serializer: U) -> io::Result<()> { extern "C" { static environ: *const *const libc::c_char; } @@ -150,74 +178,85 @@ fn write_environ< U: Write >( mut serializer: U ) -> io::Result< () > { unsafe { let mut ptr = environ; while !(*ptr).is_null() { - let string = CStr::from_ptr( *ptr ); + let string = CStr::from_ptr(*ptr); Event::Environ { - entry: string.to_bytes().into() - }.write_to_stream( &mut serializer )?; + entry: string.to_bytes().into(), + } + .write_to_stream(&mut serializer)?; - ptr = ptr.offset( 1 ); + ptr = ptr.offset(1); } } Ok(()) } -pub(crate) fn write_backtrace< U: Write >( serializer: &mut U, backtrace: Backtrace, cache: &mut BacktraceCache ) -> io::Result< u64 > { - let (id, is_new) = cache.assign_id( &backtrace ); - debug_assert_ne!( id, 0 ); +pub(crate) fn write_backtrace( + serializer: &mut U, + backtrace: Backtrace, + cache: &mut BacktraceCache, +) -> io::Result { + let (id, is_new) = cache.assign_id(&backtrace); + debug_assert_ne!(id, 0); if !is_new { - return Ok( id ); + return Ok(id); } let frames = backtrace.frames(); // TODO: Get rid of this. - let frames: Vec< _ > = frames.iter().copied().rev().collect(); + let frames: Vec<_> = frames.iter().copied().rev().collect(); - if mem::size_of::< usize >() == mem::size_of::< u32 >() { - let frames: &[u32] = unsafe { std::slice::from_raw_parts( frames.as_ptr() as *const u32, frames.len() ) }; + if mem::size_of::() == mem::size_of::() { + let frames: &[u32] = + unsafe { std::slice::from_raw_parts(frames.as_ptr() as *const u32, frames.len()) }; Event::Backtrace32 { id, - addresses: frames.into() - }.write_to_stream( serializer )?; - } else if mem::size_of::< usize >() == mem::size_of::< u64 >() { - let frames: &[u64] = unsafe { std::slice::from_raw_parts( frames.as_ptr() as *const u64, frames.len() ) }; + addresses: frames.into(), + } + .write_to_stream(serializer)?; + } else if mem::size_of::() == mem::size_of::() { + let frames: &[u64] = + unsafe { std::slice::from_raw_parts(frames.as_ptr() as *const u64, frames.len()) }; Event::Backtrace { id, - addresses: frames.into() - }.write_to_stream( serializer )?; + addresses: frames.into(), + } + .write_to_stream(serializer)?; } else { unreachable!(); } - Ok( id ) + Ok(id) } -fn write_included_files< U: Write >( serializer: &mut U ) -> io::Result< () > { +fn write_included_files(serializer: &mut U) -> io::Result<()> { let pattern = match opt::get().include_file { - Some( ref pattern ) => pattern, - None => return Ok(()) + Some(ref pattern) => pattern, + None => return Ok(()), }; - info!( "Will write any files matching the pattern: {:?}", pattern ); - match glob::glob( pattern.to_str().unwrap() ) { - Ok( paths ) => { + info!("Will write any files matching the pattern: {:?}", pattern); + match glob::glob(pattern.to_str().unwrap()) { + Ok(paths) => { let mut any = false; for path in paths { any = true; let path = match path { - Ok( path ) => path, - Err( _ ) => continue + Ok(path) => path, + Err(_) => continue, }; - info!( "Writing file: {:?}...", path ); - match mmap_file( &path, |bytes| write_file( serializer, &path.to_string_lossy(), bytes ) ) { - Ok( result ) => { + info!("Writing file: {:?}...", path); + match mmap_file(&path, |bytes| { + write_file(serializer, &path.to_string_lossy(), bytes) + }) { + Ok(result) => { result?; - }, - Err( error ) => { - warn!( "Failed to read {:?}: {}", path, error ); + } + Err(error) => { + warn!("Failed to read {:?}: {}", path, error); continue; } } @@ -226,41 +265,48 @@ fn write_included_files< U: Write >( serializer: &mut U ) -> io::Result< () > { } if !any { - info!( "No files matched the pattern!" ); + info!("No files matched the pattern!"); } - }, - Err( error ) => { - error!( "Glob of {:?} failed: {}", pattern, error ); + } + Err(error) => { + error!("Glob of {:?} failed: {}", pattern, error); } } Ok(()) } -pub fn write_initial_data< T >( id: DataId, initial_timestamp: Timestamp, mut fp: T ) -> Result< (), io::Error > where T: Write { - info!( "Writing initial header..." ); - write_header( id, initial_timestamp, &mut fp )?; - - info!( "Writing wall clock..." ); - write_wallclock( &mut fp )?; - - info!( "Writing uptime..." ); - write_uptime( &mut fp )?; - write_included_files( &mut fp )?; - - info!( "Writing environ..." ); - write_environ( &mut fp )?; - - info!( "Writing maps..." ); - write_maps( &mut fp )?; +pub fn write_initial_data( + id: DataId, + initial_timestamp: Timestamp, + mut fp: T, +) -> Result<(), io::Error> +where + T: Write, +{ + info!("Writing initial header..."); + write_header(id, initial_timestamp, &mut fp)?; + + info!("Writing wall clock..."); + write_wallclock(&mut fp)?; + + info!("Writing uptime..."); + write_uptime(&mut fp)?; + write_included_files(&mut fp)?; + + info!("Writing environ..."); + write_environ(&mut fp)?; + + info!("Writing maps..."); + write_maps(&mut fp)?; fp.flush()?; if opt::get().write_binaries_to_output { - info!( "Writing binaries..." ); - write_binaries( &mut fp )?; + info!("Writing binaries..."); + write_binaries(&mut fp)?; } - info!( "Flushing..." ); + info!("Flushing..."); fp.flush()?; Ok(()) } diff --git a/server-core/Cargo.toml b/server-core/Cargo.toml index 8a9e69ba..1c9e68e9 100644 --- a/server-core/Cargo.toml +++ b/server-core/Cargo.toml @@ -6,23 +6,23 @@ edition = "2018" [dependencies] log = "0.4" -actix = "0.8" -actix-web = { version = "1.0", default-features = false } -actix-cors = "0.1" +actix-web = { version = "4.3", default-features = false } +actix-cors = "0.6" serde = "1" serde_json = "1" serde_derive = "1" cli-core = { path = "../cli-core" } -futures = "0.1" -serde_urlencoded = "0.5" +futures = "0.3" +serde_urlencoded = "0.7" regex = "1" -bytes = "0.4" -lru = "0.6" +bytes = "1.4" +lru = "0.10" parking_lot = "0.12" common = { path = "../common" } -ahash = "0.7" +ahash = "0.8" rayon = "1" md5 = "0.7" +futures-core = "0.3.27" [build-dependencies] -semalock = "0.2" +semalock = "0.3" diff --git a/server-core/build.rs b/server-core/build.rs index 5a392fd1..3b8e6f8b 100644 --- a/server-core/build.rs +++ b/server-core/build.rs @@ -1,74 +1,78 @@ -use std::process::Command; -use std::path::{Path, PathBuf}; -use std::fs::{self, File}; -use std::io::{self, Write}; use std::env; use std::ffi::OsString; +use std::fs::{self, File}; +use std::io::{self, Write}; +use std::path::{Path, PathBuf}; +use std::process::Command; -fn grab_paths< P: AsRef< Path > >( path: P, output: &mut Vec< PathBuf > ) { +fn grab_paths>(path: P, output: &mut Vec) { let path = path.as_ref(); - let entries = match fs::read_dir( path ) { - Ok( entries ) => entries, - Err( _ ) => return + let entries = match fs::read_dir(path) { + Ok(entries) => entries, + Err(_) => return, }; for entry in entries { let entry = match entry { - Ok( entry ) => entry, - _ => continue + Ok(entry) => entry, + _ => continue, }; - output.push( entry.path().into() ); + output.push(entry.path().into()); } } -fn check_command( command: &str ) -> bool { - match Command::new( command ).args( &[ "--version" ] ).status() { - Err( ref error ) if error.kind() == io::ErrorKind::NotFound => { - false - }, - Err( error ) => { - panic!( "Cannot launch `{}`: {}", command, error ); - }, - Ok( _ ) => true +fn check_command(command: &str) -> bool { + match Command::new(command).args(&["--version"]).status() { + Err(ref error) if error.kind() == io::ErrorKind::NotFound => false, + Err(error) => { + panic!("Cannot launch `{}`: {}", command, error); + } + Ok(_) => true, } } fn main() { - let src_out_dir: PathBuf = env::var_os( "OUT_DIR" ).expect( "missing OUT_DIR" ).into(); - let crate_root: PathBuf = env::var_os( "CARGO_MANIFEST_DIR" ).expect( "missing CARGO_MANIFEST_DIR" ).into(); - let target_dir: PathBuf = env::var_os( "CARGO_TARGET_DIR" ).map( |directory| directory.into() ).unwrap_or( crate_root.join( ".." ).join( "target" ) ); + let src_out_dir: PathBuf = env::var_os("OUT_DIR").expect("missing OUT_DIR").into(); + let crate_root: PathBuf = env::var_os("CARGO_MANIFEST_DIR") + .expect("missing CARGO_MANIFEST_DIR") + .into(); + let target_dir: PathBuf = env::var_os("CARGO_TARGET_DIR") + .map(|directory| directory.into()) + .unwrap_or(crate_root.join("..").join("target")); struct Lock { - semaphore: Option< semalock::Semalock > + semaphore: Option, } impl Drop for Lock { - fn drop( &mut self ) { + fn drop(&mut self) { let _ = self.semaphore.take().unwrap().unlink(); } } - let _ = std::fs::create_dir_all( &target_dir ); + let _ = std::fs::create_dir_all(&target_dir); - let webui_dir = crate_root.join( ".." ).join( "webui" ); - let webui_out_dir = webui_dir.join( "dist" ); + let webui_dir = crate_root.join("..").join("webui"); + let webui_out_dir = webui_dir.join("dist"); { - let mut paths: Vec< PathBuf > = Vec::new(); - paths.push( webui_dir.join( ".babelrc" ) ); - paths.push( webui_dir.join( "node_modules" ) ); - paths.push( webui_dir.join( "package.json" ) ); - grab_paths( webui_dir.join( "src" ), &mut paths ); + let mut paths: Vec = Vec::new(); + paths.push(webui_dir.join(".babelrc")); + paths.push(webui_dir.join("node_modules")); + paths.push(webui_dir.join("package.json")); + grab_paths(webui_dir.join("src"), &mut paths); for path in paths { - println!( "cargo:rerun-if-changed={}", path.to_str().unwrap() ); + println!("cargo:rerun-if-changed={}", path.to_str().unwrap()); } } - let lock_path = target_dir.join( ".webui-lock" ); + let lock_path = target_dir.join(".webui-lock"); let mut lock = Lock { - semaphore: Some( semalock::Semalock::new( &lock_path ).expect( "failed to acquire a semaphore" ) ) + semaphore: Some( + semalock::Semalock::new(&lock_path).expect("failed to acquire a semaphore"), + ), }; lock.semaphore.as_mut().unwrap().with( move |_| { @@ -120,22 +124,19 @@ fn main() { println!( "cargo:warning=You're using an ancient version of Yarn; this is unsupported" ); "node_modules/.bin".into() } else { - let mut bin_path = Command::new( yarn ) - .args( &[ "bin" ] ) + let bin_path = Command::new( yarn ) + .args( &[ "bin", "parcel" ] ) .current_dir( &webui_dir ) .output() .expect( "cannot launch a child process to get Yarn's bin directory" ) .stdout; - while bin_path.ends_with( b"\n" ) { - bin_path.pop(); - } - - let bin_path = String::from_utf8( bin_path ).unwrap(); + let path = String::from_utf8(bin_path).unwrap(); + let bin_path = path.trim(); bin_path.into() }; - let mut child = Command::new( bin_path.join( "parcel" ) ) + let mut child = Command::new( bin_path ) .args( &[ "build".into(), "src/index.html".into(), diff --git a/server-core/src/byte_channel.rs b/server-core/src/byte_channel.rs index a5cc09fb..60e69f59 100644 --- a/server-core/src/byte_channel.rs +++ b/server-core/src/byte_channel.rs @@ -1,28 +1,28 @@ use std::fmt; -use std::mem; use std::io; +use std::mem; -use bytes::Bytes; use crate::streaming_channel::{self, streaming_channel}; +use bytes::Bytes; pub struct ByteSender { - buffer: Vec< u8 >, - tx: streaming_channel::Sender< Bytes > + buffer: Vec, + tx: streaming_channel::Sender, } -pub fn byte_channel() -> (ByteSender, streaming_channel::Receiver< Bytes >) { +pub fn byte_channel() -> (ByteSender, streaming_channel::Receiver) { let (tx, rx) = streaming_channel(); let tx = ByteSender { buffer: Vec::new(), - tx + tx, }; (tx, rx) } impl ByteSender { - fn write_buffer( &mut self, buffer: &[u8] ) -> Result< (), () > { - self.buffer.extend_from_slice( buffer ); + fn write_buffer(&mut self, buffer: &[u8]) -> Result<(), ()> { + self.buffer.extend_from_slice(buffer); if self.buffer.len() >= 128 * 1024 { self.flush_buffer()?; } @@ -30,38 +30,41 @@ impl ByteSender { Ok(()) } - fn flush_buffer( &mut self ) -> Result< (), () > { + fn flush_buffer(&mut self) -> Result<(), ()> { if self.buffer.is_empty() { return Ok(()); } - let mut vec = Vec::with_capacity( self.buffer.capacity() ); - mem::swap( &mut vec, &mut self.buffer ); - return self.tx.send( vec.into() ); + let mut vec = Vec::with_capacity(self.buffer.capacity()); + mem::swap(&mut vec, &mut self.buffer); + return self.tx.send(vec.into()); } } impl Drop for ByteSender { - fn drop( &mut self ) { + fn drop(&mut self) { let _ = self.flush_buffer(); } } impl fmt::Write for ByteSender { #[inline] - fn write_str( &mut self, s: &str ) -> Result< (), fmt::Error > { - self.write_buffer( s.as_bytes() ).map_err( |_| fmt::Error ) + fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> { + self.write_buffer(s.as_bytes()).map_err(|_| fmt::Error) } } impl io::Write for ByteSender { #[inline] - fn write( &mut self, buffer: &[u8] ) -> io::Result< usize > { - self.write_buffer( buffer ).map_err( |_| io::Error::new( io::ErrorKind::Other, "write failed" ) ).map( |_| buffer.len() ) + fn write(&mut self, buffer: &[u8]) -> io::Result { + self.write_buffer(buffer) + .map_err(|_| io::Error::new(io::ErrorKind::Other, "write failed")) + .map(|_| buffer.len()) } #[inline] - fn flush( &mut self ) -> io::Result< () > { - self.flush_buffer().map_err( |_| io::Error::new( io::ErrorKind::Other, "write failed" ) ) + fn flush(&mut self) -> io::Result<()> { + self.flush_buffer() + .map_err(|_| io::Error::new(io::ErrorKind::Other, "write failed")) } } diff --git a/server-core/src/filter.rs b/server-core/src/filter.rs index a98094c9..3d606985 100644 --- a/server-core/src/filter.rs +++ b/server-core/src/filter.rs @@ -1,97 +1,96 @@ -use std::sync::Arc; use ahash::AHashMap as HashMap; use ahash::AHashSet as HashSet; use parking_lot::Mutex; +use std::sync::Arc; use regex::{self, Regex}; use cli_core::{ - Allocation, - AllocationId, - BacktraceId, - Data, - Timestamp, - Compile, + Allocation, AllocationId, BacktraceId, Compile, Data, EvalOutput, Map, MapId, Timestamp, TryMatch, - MapId, - Map, - EvalOutput, }; use crate::protocol; #[derive(Clone, Debug)] pub struct GroupFilter { - pub interval_min: Option< Timestamp >, - pub interval_max: Option< Timestamp >, - pub leaked_allocations_min: Option< protocol::NumberOrPercentage >, - pub leaked_allocations_max: Option< protocol::NumberOrPercentage >, + pub interval_min: Option, + pub interval_max: Option, + pub leaked_allocations_min: Option, + pub leaked_allocations_max: Option, pub allocations_min: usize, - pub allocations_max: usize + pub allocations_max: usize, } #[derive(Clone, Debug)] pub struct BacktraceFilter { pub backtrace_depth_min: usize, pub backtrace_depth_max: usize, - pub function_regex: Option< Regex >, - pub source_regex: Option< Regex >, - pub negative_function_regex: Option< Regex >, - pub negative_source_regex: Option< Regex >, + pub function_regex: Option, + pub source_regex: Option, + pub negative_function_regex: Option, + pub negative_source_regex: Option, } -impl From< crate::protocol::NumberOrPercentage > for cli_core::NumberOrFractionOfTotal { - fn from( value: crate::protocol::NumberOrPercentage ) -> Self { +impl From for cli_core::NumberOrFractionOfTotal { + fn from(value: crate::protocol::NumberOrPercentage) -> Self { match value { - crate::protocol::NumberOrPercentage::Absolute( value ) => cli_core::NumberOrFractionOfTotal::Number( value as _ ), - crate::protocol::NumberOrPercentage::Percent( value ) => cli_core::NumberOrFractionOfTotal::Fraction( value as f64 / 100.0 ) + crate::protocol::NumberOrPercentage::Absolute(value) => { + cli_core::NumberOrFractionOfTotal::Number(value as _) + } + crate::protocol::NumberOrPercentage::Percent(value) => { + cli_core::NumberOrFractionOfTotal::Fraction(value as f64 / 100.0) + } } } } -fn run_custom_filter( data: &Arc< Data >, custom_filter: &protocol::CustomFilter ) -> Result< Option< EvalOutput >, cli_core::script::EvalError > { - if let Some( ref custom_filter ) = custom_filter.custom_filter { +fn run_custom_filter( + data: &Arc, + custom_filter: &protocol::CustomFilter, +) -> Result, cli_core::script::EvalError> { + if let Some(ref custom_filter) = custom_filter.custom_filter { if custom_filter.is_empty() { - return Ok( None ); + return Ok(None); } let args = cli_core::script::EngineArgs { - data: Some( data.clone() ), - .. cli_core::script::EngineArgs::default() + data: Some(data.clone()), + ..cli_core::script::EngineArgs::default() }; - let env = Arc::new( Mutex::new( cli_core::script::VirtualEnvironment::new() ) ); - let engine = cli_core::script::Engine::new( env.clone(), args ); - return Ok( engine.run( &custom_filter )? ); + let env = Arc::new(Mutex::new(cli_core::script::VirtualEnvironment::new())); + let engine = cli_core::script::Engine::new(env.clone(), args); + return Ok(engine.run(&custom_filter)?); } - Ok( None ) + Ok(None) } -fn run_custom_allocation_filter( data: &Arc< Data >, custom_filter: &protocol::CustomFilter ) -> Result< Option< Arc< HashSet< AllocationId > > >, cli_core::script::EvalError > { - match run_custom_filter( data, custom_filter )? { - None => { - Ok( None ) - }, - Some( EvalOutput::AllocationList( mut list ) ) => { - Ok( Some( Arc::new( list.allocation_ids().iter().copied().collect() ) ) ) - }, - Some( EvalOutput::MapList( .. ) ) => { - Err( "expected 'AllocationList', got 'MapList'".into() ) - } +fn run_custom_allocation_filter( + data: &Arc, + custom_filter: &protocol::CustomFilter, +) -> Result>>, cli_core::script::EvalError> { + match run_custom_filter(data, custom_filter)? { + None => Ok(None), + Some(EvalOutput::AllocationList(mut list)) => Ok(Some(Arc::new( + list.allocation_ids().iter().copied().collect(), + ))), + Some(EvalOutput::MapList(..)) => Err("expected 'AllocationList', got 'MapList'".into()), } } -fn run_custom_map_filter( data: &Arc< Data >, custom_filter: &protocol::CustomFilter ) -> Result< Option< Arc< HashSet< MapId > > >, cli_core::script::EvalError > { - match run_custom_filter( data, custom_filter )? { - None => { - Ok( None ) - }, - Some( EvalOutput::AllocationList( .. ) ) => { - Err( "expected 'MapList', got 'AllocationList'".into() ) - }, - Some( EvalOutput::MapList( mut list ) ) => { - Ok( Some( Arc::new( list.map_ids().iter().copied().collect() ) ) ) +fn run_custom_map_filter( + data: &Arc, + custom_filter: &protocol::CustomFilter, +) -> Result>>, cli_core::script::EvalError> { + match run_custom_filter(data, custom_filter)? { + None => Ok(None), + Some(EvalOutput::AllocationList(..)) => { + Err("expected 'MapList', got 'AllocationList'".into()) + } + Some(EvalOutput::MapList(mut list)) => { + Ok(Some(Arc::new(list.map_ids().iter().copied().collect()))) } } } @@ -99,18 +98,18 @@ fn run_custom_map_filter( data: &Arc< Data >, custom_filter: &protocol::CustomFi #[derive(Clone)] pub struct AllocationFilter { filter: cli_core::CompiledAllocationFilter, - custom_filter: Option< Arc< HashSet< AllocationId > > > + custom_filter: Option>>, } impl AllocationFilter { - pub fn try_match( &self, data: &Data, id: AllocationId, allocation: &Allocation ) -> bool { - if let Some( ref custom_filter ) = self.custom_filter { - if !custom_filter.contains( &id ) { + pub fn try_match(&self, data: &Data, id: AllocationId, allocation: &Allocation) -> bool { + if let Some(ref custom_filter) = self.custom_filter { + if !custom_filter.contains(&id) { return false; } } - if !self.filter.try_match( data, allocation ) { + if !self.filter.try_match(data, allocation) { return false; } @@ -119,23 +118,34 @@ impl AllocationFilter { } pub fn prepare_allocation_filter( - data: &Arc< Data >, + data: &Arc, filter: &protocol::AllocFilter, - custom_filter: &protocol::CustomFilter -) -> Result< AllocationFilter, PrepareFilterError > { - let filter = prepare_raw_allocation_filter( data, filter )?.compile( data ); - let custom_filter = run_custom_allocation_filter( data, custom_filter ).map_err( |error| PrepareFilterError::InvalidCustomFilter( error.message ) )?; - - Ok( AllocationFilter { filter, custom_filter } ) + custom_filter: &protocol::CustomFilter, +) -> Result { + let filter = prepare_raw_allocation_filter(data, filter)?.compile(data); + let custom_filter = run_custom_allocation_filter(data, custom_filter) + .map_err(|error| PrepareFilterError::InvalidCustomFilter(error.message))?; + + Ok(AllocationFilter { + filter, + custom_filter, + }) } -pub fn prepare_raw_allocation_filter( data: &Data, filter: &protocol::AllocFilter ) -> Result< cli_core::AllocationFilter, PrepareFilterError > { +pub fn prepare_raw_allocation_filter( + data: &Data, + filter: &protocol::AllocFilter, +) -> Result { use cli_core::Duration; let mut output = cli_core::RawAllocationFilter::default(); - output.common_filter.only_allocated_after_at_least = filter.from.map( |ts| Duration( ts.to_timestamp( data.initial_timestamp(), data.last_timestamp() ) ) ); - output.common_filter.only_allocated_until_at_most = filter.to.map( |ts| Duration( ts.to_timestamp( data.initial_timestamp(), data.last_timestamp() ) ) ); + output.common_filter.only_allocated_after_at_least = filter + .from + .map(|ts| Duration(ts.to_timestamp(data.initial_timestamp(), data.last_timestamp()))); + output.common_filter.only_allocated_until_at_most = filter + .to + .map(|ts| Duration(ts.to_timestamp(data.initial_timestamp(), data.last_timestamp()))); output.common_filter.only_address_at_least = filter.address_min; output.common_filter.only_address_at_most = filter.address_max; output.common_filter.only_larger_or_equal = filter.size_min; @@ -145,126 +155,163 @@ pub fn prepare_raw_allocation_filter( data: &Data, filter: &protocol::AllocFilte output.only_last_size_larger_or_equal = filter.last_size_min; output.only_last_size_smaller_or_equal = filter.last_size_max; - if let Some( only_alive_at ) = filter.alive_at.map( |ts| Duration( ts.to_timestamp( data.initial_timestamp(), data.last_timestamp() ) ) ) { - output.common_filter.only_alive_at.push( only_alive_at ); + if let Some(only_alive_at) = filter + .alive_at + .map(|ts| Duration(ts.to_timestamp(data.initial_timestamp(), data.last_timestamp()))) + { + output.common_filter.only_alive_at.push(only_alive_at); } - if let Some( only_alive_at ) = filter.alive_at_2.map( |ts| Duration( ts.to_timestamp( data.initial_timestamp(), data.last_timestamp() ) ) ) { - output.common_filter.only_alive_at.push( only_alive_at ); + if let Some(only_alive_at) = filter + .alive_at_2 + .map(|ts| Duration(ts.to_timestamp(data.initial_timestamp(), data.last_timestamp()))) + { + output.common_filter.only_alive_at.push(only_alive_at); } - if let Some( from_map ) = filter.from_map { - output.only_from_maps = Some( std::iter::once( MapId( from_map ) ).collect() ); + if let Some(from_map) = filter.from_map { + output.only_from_maps = Some(std::iter::once(MapId(from_map)).collect()); } - output.common_filter.only_alive_for_at_least = filter.lifetime_min.map( |interval| Duration( interval.0 ) ); - output.common_filter.only_alive_for_at_most = filter.lifetime_max.map( |interval| Duration( interval.0 ) ); + output.common_filter.only_alive_for_at_least = + filter.lifetime_min.map(|interval| Duration(interval.0)); + output.common_filter.only_alive_for_at_most = + filter.lifetime_max.map(|interval| Duration(interval.0)); - output.backtrace_filter.only_backtrace_length_at_least = filter.backtrace_depth_min.map( |value| value as usize ); - output.backtrace_filter.only_backtrace_length_at_most = filter.backtrace_depth_max.map( |value| value as usize ); + output.backtrace_filter.only_backtrace_length_at_least = + filter.backtrace_depth_min.map(|value| value as usize); + output.backtrace_filter.only_backtrace_length_at_most = + filter.backtrace_depth_max.map(|value| value as usize); - if let Some( id ) = filter.backtraces { - output.backtrace_filter.only_matching_backtraces = Some( std::iter::once( BacktraceId::new( id ) ).collect() ); + if let Some(id) = filter.backtraces { + output.backtrace_filter.only_matching_backtraces = + Some(std::iter::once(BacktraceId::new(id)).collect()); } - if let Some( id ) = filter.deallocation_backtraces { - output.backtrace_filter.only_matching_deallocation_backtraces = Some( std::iter::once( BacktraceId::new( id ) ).collect() ); + if let Some(id) = filter.deallocation_backtraces { + output + .backtrace_filter + .only_matching_deallocation_backtraces = + Some(std::iter::once(BacktraceId::new(id)).collect()); } match filter.mmaped { - None => {}, - Some( protocol::MmapedFilter::Yes ) => output.only_ptmalloc_mmaped = true, - Some( protocol::MmapedFilter::No ) => output.only_ptmalloc_not_mmaped = true + None => {} + Some(protocol::MmapedFilter::Yes) => output.only_ptmalloc_mmaped = true, + Some(protocol::MmapedFilter::No) => output.only_ptmalloc_not_mmaped = true, } match filter.jemalloc { - None => {}, - Some( protocol::JemallocFilter::Yes ) => output.only_jemalloc = true, - Some( protocol::JemallocFilter::No ) => output.only_not_jemalloc = true + None => {} + Some(protocol::JemallocFilter::Yes) => output.only_jemalloc = true, + Some(protocol::JemallocFilter::No) => output.only_not_jemalloc = true, } match filter.arena { - None => {}, - Some( protocol::ArenaFilter::Main ) => output.only_ptmalloc_from_main_arena = true, - Some( protocol::ArenaFilter::NonMain ) => output.only_ptmalloc_not_from_main_arena = true + None => {} + Some(protocol::ArenaFilter::Main) => output.only_ptmalloc_from_main_arena = true, + Some(protocol::ArenaFilter::NonMain) => output.only_ptmalloc_not_from_main_arena = true, } - if let Some( ref pattern ) = filter.function_regex { + if let Some(ref pattern) = filter.function_regex { output.backtrace_filter.only_passing_through_function = Some( - Regex::new( &pattern ).map_err( |err| PrepareFilterError::InvalidRegex( "function_regex", err ) )? + Regex::new(&pattern) + .map_err(|err| PrepareFilterError::InvalidRegex("function_regex", err))?, ); } - if let Some( ref pattern ) = filter.negative_function_regex { + if let Some(ref pattern) = filter.negative_function_regex { output.backtrace_filter.only_not_passing_through_function = Some( - Regex::new( &pattern ).map_err( |err| PrepareFilterError::InvalidRegex( "negative_function_regex", err ) )? + Regex::new(&pattern) + .map_err(|err| PrepareFilterError::InvalidRegex("negative_function_regex", err))?, ); } - if let Some( ref pattern ) = filter.source_regex { + if let Some(ref pattern) = filter.source_regex { output.backtrace_filter.only_passing_through_source = Some( - Regex::new( &pattern ).map_err( |err| PrepareFilterError::InvalidRegex( "source_regex", err ) )? + Regex::new(&pattern) + .map_err(|err| PrepareFilterError::InvalidRegex("source_regex", err))?, ); } - if let Some( ref pattern ) = filter.negative_source_regex { + if let Some(ref pattern) = filter.negative_source_regex { output.backtrace_filter.only_not_passing_through_source = Some( - Regex::new( &pattern ).map_err( |err| PrepareFilterError::InvalidRegex( "negative_source_regex", err ) )? + Regex::new(&pattern) + .map_err(|err| PrepareFilterError::InvalidRegex("negative_source_regex", err))?, ); } output.only_with_marker = filter.marker; - output.only_group_interval_at_least = filter.group_interval_min.map( |ts| Duration( ts.to_timestamp( data.initial_timestamp(), data.last_timestamp() ) ) ); - output.only_group_interval_at_most = filter.group_interval_max.map( |ts| Duration( ts.to_timestamp( data.initial_timestamp(), data.last_timestamp() ) ) ); - output.only_group_max_total_usage_first_seen_at_least = filter.group_max_total_usage_first_seen_min.map( |ts| Duration( ts.to_timestamp( data.initial_timestamp(), data.last_timestamp() ) ) ); - output.only_group_max_total_usage_first_seen_at_most = filter.group_max_total_usage_first_seen_max.map( |ts| Duration( ts.to_timestamp( data.initial_timestamp(), data.last_timestamp() ) ) ); - output.only_group_allocations_at_least = filter.group_allocations_min.map( |value| value as usize ); - output.only_group_allocations_at_most = filter.group_allocations_max.map( |value| value as usize ); - output.only_group_leaked_allocations_at_least = filter.group_leaked_allocations_min.map( |value| value.into() ); - output.only_group_leaked_allocations_at_most = filter.group_leaked_allocations_max.map( |value| value.into() ); + output.only_group_interval_at_least = filter + .group_interval_min + .map(|ts| Duration(ts.to_timestamp(data.initial_timestamp(), data.last_timestamp()))); + output.only_group_interval_at_most = filter + .group_interval_max + .map(|ts| Duration(ts.to_timestamp(data.initial_timestamp(), data.last_timestamp()))); + output.only_group_max_total_usage_first_seen_at_least = filter + .group_max_total_usage_first_seen_min + .map(|ts| Duration(ts.to_timestamp(data.initial_timestamp(), data.last_timestamp()))); + output.only_group_max_total_usage_first_seen_at_most = filter + .group_max_total_usage_first_seen_max + .map(|ts| Duration(ts.to_timestamp(data.initial_timestamp(), data.last_timestamp()))); + output.only_group_allocations_at_least = + filter.group_allocations_min.map(|value| value as usize); + output.only_group_allocations_at_most = + filter.group_allocations_max.map(|value| value as usize); + output.only_group_leaked_allocations_at_least = filter + .group_leaked_allocations_min + .map(|value| value.into()); + output.only_group_leaked_allocations_at_most = filter + .group_leaked_allocations_max + .map(|value| value.into()); output.only_chain_length_at_least = filter.chain_length_min; output.only_chain_length_at_most = filter.chain_length_max; - output.only_chain_alive_for_at_least = filter.chain_lifetime_min.map( |interval| Duration( interval.0 ) ); - output.only_chain_alive_for_at_most = filter.chain_lifetime_max.map( |interval| Duration( interval.0 ) ); + output.only_chain_alive_for_at_least = filter + .chain_lifetime_min + .map(|interval| Duration(interval.0)); + output.only_chain_alive_for_at_most = filter + .chain_lifetime_max + .map(|interval| Duration(interval.0)); output.only_position_in_chain_at_least = filter.position_in_chain_min; output.only_position_in_chain_at_most = filter.position_in_chain_max; - match filter.lifetime.unwrap_or( protocol::LifetimeFilter::All ) { - protocol::LifetimeFilter::All => {}, + match filter.lifetime.unwrap_or(protocol::LifetimeFilter::All) { + protocol::LifetimeFilter::All => {} protocol::LifetimeFilter::OnlyLeaked => { output.common_filter.only_leaked = true; - }, + } protocol::LifetimeFilter::OnlyChainLeaked => { output.only_chain_leaked = true; - }, + } protocol::LifetimeFilter::OnlyTemporary => { output.common_filter.only_temporary = true; - }, + } protocol::LifetimeFilter::OnlyWholeGroupLeaked => { - output.only_group_leaked_allocations_at_least = Some( cli_core::NumberOrFractionOfTotal::Fraction( 1.0 ) ); + output.only_group_leaked_allocations_at_least = + Some(cli_core::NumberOrFractionOfTotal::Fraction(1.0)); } } let output: cli_core::AllocationFilter = output.into(); - Ok( output ) + Ok(output) } #[derive(Clone)] pub struct MapFilter { filter: cli_core::CompiledMapFilter, - custom_filter: Option< Arc< HashSet< MapId > > > + custom_filter: Option>>, } impl MapFilter { - pub fn try_match( &self, data: &Data, id: MapId, allocation: &Map ) -> bool { - if let Some( ref custom_filter ) = self.custom_filter { - if !custom_filter.contains( &id ) { + pub fn try_match(&self, data: &Data, id: MapId, allocation: &Map) -> bool { + if let Some(ref custom_filter) = self.custom_filter { + if !custom_filter.contains(&id) { return false; } } - if !self.filter.try_match( data, allocation ) { + if !self.filter.try_match(data, allocation) { return false; } @@ -273,85 +320,114 @@ impl MapFilter { } pub fn prepare_map_filter( - data: &Arc< Data >, + data: &Arc, filter: &protocol::MapFilter, - custom_filter: &protocol::CustomFilter -) -> Result< MapFilter, PrepareFilterError > { - let filter = prepare_raw_map_filter( data, filter )?.compile( data ); - let custom_filter = run_custom_map_filter( data, custom_filter ).map_err( |error| PrepareFilterError::InvalidCustomFilter( error.message ) )?; - - Ok( MapFilter { filter, custom_filter } ) + custom_filter: &protocol::CustomFilter, +) -> Result { + let filter = prepare_raw_map_filter(data, filter)?.compile(data); + let custom_filter = run_custom_map_filter(data, custom_filter) + .map_err(|error| PrepareFilterError::InvalidCustomFilter(error.message))?; + + Ok(MapFilter { + filter, + custom_filter, + }) } -pub fn prepare_raw_map_filter( data: &Data, filter: &protocol::MapFilter ) -> Result< cli_core::MapFilter, PrepareFilterError > { +pub fn prepare_raw_map_filter( + data: &Data, + filter: &protocol::MapFilter, +) -> Result { use cli_core::Duration; let mut output = cli_core::RawMapFilter::default(); // TODO: Deduplicate this with the allocation filter. - output.common_filter.only_allocated_after_at_least = filter.from.map( |ts| Duration( ts.to_timestamp( data.initial_timestamp(), data.last_timestamp() ) ) ); - output.common_filter.only_allocated_until_at_most = filter.to.map( |ts| Duration( ts.to_timestamp( data.initial_timestamp(), data.last_timestamp() ) ) ); + output.common_filter.only_allocated_after_at_least = filter + .from + .map(|ts| Duration(ts.to_timestamp(data.initial_timestamp(), data.last_timestamp()))); + output.common_filter.only_allocated_until_at_most = filter + .to + .map(|ts| Duration(ts.to_timestamp(data.initial_timestamp(), data.last_timestamp()))); output.common_filter.only_address_at_least = filter.address_min; output.common_filter.only_address_at_most = filter.address_max; output.common_filter.only_larger_or_equal = filter.size_min; output.common_filter.only_smaller_or_equal = filter.size_max; - if let Some( only_alive_at ) = filter.alive_at.map( |ts| Duration( ts.to_timestamp( data.initial_timestamp(), data.last_timestamp() ) ) ) { - output.common_filter.only_alive_at.push( only_alive_at ); + if let Some(only_alive_at) = filter + .alive_at + .map(|ts| Duration(ts.to_timestamp(data.initial_timestamp(), data.last_timestamp()))) + { + output.common_filter.only_alive_at.push(only_alive_at); } - if let Some( only_alive_at ) = filter.alive_at_2.map( |ts| Duration( ts.to_timestamp( data.initial_timestamp(), data.last_timestamp() ) ) ) { - output.common_filter.only_alive_at.push( only_alive_at ); + if let Some(only_alive_at) = filter + .alive_at_2 + .map(|ts| Duration(ts.to_timestamp(data.initial_timestamp(), data.last_timestamp()))) + { + output.common_filter.only_alive_at.push(only_alive_at); } - output.common_filter.only_alive_for_at_least = filter.lifetime_min.map( |interval| Duration( interval.0 ) ); - output.common_filter.only_alive_for_at_most = filter.lifetime_max.map( |interval| Duration( interval.0 ) ); + output.common_filter.only_alive_for_at_least = + filter.lifetime_min.map(|interval| Duration(interval.0)); + output.common_filter.only_alive_for_at_most = + filter.lifetime_max.map(|interval| Duration(interval.0)); - output.backtrace_filter.only_backtrace_length_at_least = filter.backtrace_depth_min.map( |value| value as usize ); - output.backtrace_filter.only_backtrace_length_at_most = filter.backtrace_depth_max.map( |value| value as usize ); + output.backtrace_filter.only_backtrace_length_at_least = + filter.backtrace_depth_min.map(|value| value as usize); + output.backtrace_filter.only_backtrace_length_at_most = + filter.backtrace_depth_max.map(|value| value as usize); - if let Some( id ) = filter.backtraces { - output.backtrace_filter.only_matching_backtraces = Some( std::iter::once( BacktraceId::new( id ) ).collect() ); + if let Some(id) = filter.backtraces { + output.backtrace_filter.only_matching_backtraces = + Some(std::iter::once(BacktraceId::new(id)).collect()); } - if let Some( id ) = filter.deallocation_backtraces { - output.backtrace_filter.only_matching_deallocation_backtraces = Some( std::iter::once( BacktraceId::new( id ) ).collect() ); + if let Some(id) = filter.deallocation_backtraces { + output + .backtrace_filter + .only_matching_deallocation_backtraces = + Some(std::iter::once(BacktraceId::new(id)).collect()); } - if let Some( ref pattern ) = filter.function_regex { + if let Some(ref pattern) = filter.function_regex { output.backtrace_filter.only_passing_through_function = Some( - Regex::new( &pattern ).map_err( |err| PrepareFilterError::InvalidRegex( "function_regex", err ) )? + Regex::new(&pattern) + .map_err(|err| PrepareFilterError::InvalidRegex("function_regex", err))?, ); } - if let Some( ref pattern ) = filter.negative_function_regex { + if let Some(ref pattern) = filter.negative_function_regex { output.backtrace_filter.only_not_passing_through_function = Some( - Regex::new( &pattern ).map_err( |err| PrepareFilterError::InvalidRegex( "negative_function_regex", err ) )? + Regex::new(&pattern) + .map_err(|err| PrepareFilterError::InvalidRegex("negative_function_regex", err))?, ); } - if let Some( ref pattern ) = filter.source_regex { + if let Some(ref pattern) = filter.source_regex { output.backtrace_filter.only_passing_through_source = Some( - Regex::new( &pattern ).map_err( |err| PrepareFilterError::InvalidRegex( "source_regex", err ) )? + Regex::new(&pattern) + .map_err(|err| PrepareFilterError::InvalidRegex("source_regex", err))?, ); } - if let Some( ref pattern ) = filter.negative_source_regex { + if let Some(ref pattern) = filter.negative_source_regex { output.backtrace_filter.only_not_passing_through_source = Some( - Regex::new( &pattern ).map_err( |err| PrepareFilterError::InvalidRegex( "negative_source_regex", err ) )? + Regex::new(&pattern) + .map_err(|err| PrepareFilterError::InvalidRegex("negative_source_regex", err))?, ); } - match filter.lifetime.unwrap_or( protocol::LifetimeFilter::All ) { - protocol::LifetimeFilter::All => {}, + match filter.lifetime.unwrap_or(protocol::LifetimeFilter::All) { + protocol::LifetimeFilter::All => {} protocol::LifetimeFilter::OnlyLeaked => { output.common_filter.only_leaked = true; - }, + } protocol::LifetimeFilter::OnlyChainLeaked => { unimplemented!() - }, + } protocol::LifetimeFilter::OnlyTemporary => { output.common_filter.only_temporary = true; - }, + } protocol::LifetimeFilter::OnlyWholeGroupLeaked => { unimplemented!() } @@ -359,163 +435,185 @@ pub fn prepare_raw_map_filter( data: &Data, filter: &protocol::MapFilter ) -> Re output.only_peak_rss_at_least = filter.peak_rss_min; output.only_peak_rss_at_most = filter.peak_rss_max; - output.only_jemalloc = filter.jemalloc == Some( protocol::BoolFilter::OnlyTrue ); - output.only_not_jemalloc = filter.jemalloc == Some( protocol::BoolFilter::OnlyFalse ); - output.only_bytehound = filter.bytehound == Some( protocol::BoolFilter::OnlyTrue ); - output.only_not_bytehound = filter.bytehound == Some( protocol::BoolFilter::OnlyFalse ); - output.only_readable = filter.readable == Some( protocol::BoolFilter::OnlyTrue ); - output.only_not_readable = filter.readable == Some( protocol::BoolFilter::OnlyFalse ); - output.only_writable = filter.writable == Some( protocol::BoolFilter::OnlyTrue ); - output.only_not_writable = filter.writable == Some( protocol::BoolFilter::OnlyFalse ); - output.only_executable = filter.executable == Some( protocol::BoolFilter::OnlyTrue ); - output.only_not_executable = filter.executable == Some( protocol::BoolFilter::OnlyFalse ); + output.only_jemalloc = filter.jemalloc == Some(protocol::BoolFilter::OnlyTrue); + output.only_not_jemalloc = filter.jemalloc == Some(protocol::BoolFilter::OnlyFalse); + output.only_bytehound = filter.bytehound == Some(protocol::BoolFilter::OnlyTrue); + output.only_not_bytehound = filter.bytehound == Some(protocol::BoolFilter::OnlyFalse); + output.only_readable = filter.readable == Some(protocol::BoolFilter::OnlyTrue); + output.only_not_readable = filter.readable == Some(protocol::BoolFilter::OnlyFalse); + output.only_writable = filter.writable == Some(protocol::BoolFilter::OnlyTrue); + output.only_not_writable = filter.writable == Some(protocol::BoolFilter::OnlyFalse); + output.only_executable = filter.executable == Some(protocol::BoolFilter::OnlyTrue); + output.only_not_executable = filter.executable == Some(protocol::BoolFilter::OnlyFalse); let output: cli_core::MapFilter = output.into(); - Ok( output ) + Ok(output) } pub enum PrepareFilterError { - InvalidRegex( &'static str, regex::Error ), - InvalidCustomFilter( String ) + InvalidRegex(&'static str, regex::Error), + InvalidCustomFilter(String), } -pub fn prepare_backtrace_filter( filter: &protocol::BacktraceFilter ) -> Result< BacktraceFilter, PrepareFilterError > { - let function_regex = if let Some( ref pattern ) = filter.function_regex { - Some( Regex::new( pattern ).map_err( |err| PrepareFilterError::InvalidRegex( "function_regex", err ) )? ) +pub fn prepare_backtrace_filter( + filter: &protocol::BacktraceFilter, +) -> Result { + let function_regex = if let Some(ref pattern) = filter.function_regex { + Some( + Regex::new(pattern) + .map_err(|err| PrepareFilterError::InvalidRegex("function_regex", err))?, + ) } else { None }; - let source_regex = if let Some( ref pattern ) = filter.source_regex { - Some( Regex::new( pattern ).map_err( |err| PrepareFilterError::InvalidRegex( "source_regex", err ) )? ) + let source_regex = if let Some(ref pattern) = filter.source_regex { + Some( + Regex::new(pattern) + .map_err(|err| PrepareFilterError::InvalidRegex("source_regex", err))?, + ) } else { None }; - let negative_function_regex = if let Some( ref pattern ) = filter.negative_function_regex { - Some( Regex::new( pattern ).map_err( |err| PrepareFilterError::InvalidRegex( "negative_function_regex", err ) )? ) + let negative_function_regex = if let Some(ref pattern) = filter.negative_function_regex { + Some( + Regex::new(pattern) + .map_err(|err| PrepareFilterError::InvalidRegex("negative_function_regex", err))?, + ) } else { None }; - let negative_source_regex = if let Some( ref pattern ) = filter.negative_source_regex { - Some( Regex::new( pattern ).map_err( |err| PrepareFilterError::InvalidRegex( "negative_source_regex", err ) )? ) + let negative_source_regex = if let Some(ref pattern) = filter.negative_source_regex { + Some( + Regex::new(pattern) + .map_err(|err| PrepareFilterError::InvalidRegex("negative_source_regex", err))?, + ) } else { None }; let filter = BacktraceFilter { - backtrace_depth_min: filter.backtrace_depth_min.unwrap_or( 0 ) as usize, - backtrace_depth_max: filter.backtrace_depth_max.unwrap_or( std::u32::MAX ) as usize, + backtrace_depth_min: filter.backtrace_depth_min.unwrap_or(0) as usize, + backtrace_depth_max: filter.backtrace_depth_max.unwrap_or(std::u32::MAX) as usize, function_regex, source_regex, negative_function_regex, - negative_source_regex + negative_source_regex, }; - Ok( filter ) + Ok(filter) } -pub fn match_backtrace< 'a >( +pub fn match_backtrace<'a>( data: &Data, - positive_cache: &mut HashMap< crate::FrameId, bool >, - negative_cache: &mut HashMap< crate::FrameId, bool >, + positive_cache: &mut HashMap, + negative_cache: &mut HashMap, filter: &BacktraceFilter, - backtrace: impl ExactSizeIterator< Item = (crate::FrameId, &'a crate::Frame) > + backtrace: impl ExactSizeIterator, ) -> bool { - if backtrace.len() < filter.backtrace_depth_min || backtrace.len() > filter.backtrace_depth_max { + if backtrace.len() < filter.backtrace_depth_min || backtrace.len() > filter.backtrace_depth_max + { return false; } let mut positive_matched = filter.function_regex.is_none() && filter.source_regex.is_none(); let mut negative_matched = false; - let check_negative = filter.negative_function_regex.is_some() || filter.negative_source_regex.is_some(); + let check_negative = + filter.negative_function_regex.is_some() || filter.negative_source_regex.is_some(); for (frame_id, frame) in backtrace { - let check_positive = - if positive_matched { - false - } else if let Some( &cached_result ) = positive_cache.get( &frame_id ) { - positive_matched = cached_result; - false - } else { - true - }; + let check_positive = if positive_matched { + false + } else if let Some(&cached_result) = positive_cache.get(&frame_id) { + positive_matched = cached_result; + false + } else { + true + }; if positive_matched && !check_negative { break; } let mut function = None; - if (check_positive && filter.function_regex.is_some()) || filter.negative_function_regex.is_some() { - function = frame.function().or_else( || frame.raw_function() ).map( |id| data.interner().resolve( id ).unwrap() ); + if (check_positive && filter.function_regex.is_some()) + || filter.negative_function_regex.is_some() + { + function = frame + .function() + .or_else(|| frame.raw_function()) + .map(|id| data.interner().resolve(id).unwrap()); } let mut source = None; - if (check_positive && filter.source_regex.is_some()) || filter.negative_source_regex.is_some() { - source = frame.source().map( |id| data.interner().resolve( id ).unwrap() ) + if (check_positive && filter.source_regex.is_some()) + || filter.negative_source_regex.is_some() + { + source = frame + .source() + .map(|id| data.interner().resolve(id).unwrap()) } if check_positive { - let matched_function = - if let Some( regex ) = filter.function_regex.as_ref() { - if let Some( ref function ) = function { - regex.is_match( function ) - } else { - false - } + let matched_function = if let Some(regex) = filter.function_regex.as_ref() { + if let Some(ref function) = function { + regex.is_match(function) } else { - true - }; - - let matched_source = - if let Some( regex ) = filter.source_regex.as_ref() { - if let Some( ref source ) = source { - regex.is_match( source ) - } else { - false - } + false + } + } else { + true + }; + + let matched_source = if let Some(regex) = filter.source_regex.as_ref() { + if let Some(ref source) = source { + regex.is_match(source) } else { - true - }; + false + } + } else { + true + }; positive_matched = matched_function && matched_source; - positive_cache.insert( frame_id, positive_matched ); + positive_cache.insert(frame_id, positive_matched); } if check_negative { - match negative_cache.get( &frame_id ).cloned() { - Some( true ) => { + match negative_cache.get(&frame_id).cloned() { + Some(true) => { negative_matched = true; break; - }, - Some( false ) => { + } + Some(false) => { continue; - }, + } None => {} } - if let Some( regex ) = filter.negative_function_regex.as_ref() { - if let Some( ref function ) = function { - if regex.is_match( function ) { - negative_cache.insert( frame_id, true ); + if let Some(regex) = filter.negative_function_regex.as_ref() { + if let Some(ref function) = function { + if regex.is_match(function) { + negative_cache.insert(frame_id, true); negative_matched = true; break; } } } - if let Some( regex ) = filter.negative_source_regex.as_ref() { - if let Some( ref source ) = source { - if regex.is_match( source ) { - negative_cache.insert( frame_id, true ); + if let Some(regex) = filter.negative_source_regex.as_ref() { + if let Some(ref source) = source { + if regex.is_match(source) { + negative_cache.insert(frame_id, true); negative_matched = true; break; } } } - negative_cache.insert( frame_id, false ); + negative_cache.insert(frame_id, false); } } diff --git a/server-core/src/itertools.rs b/server-core/src/itertools.rs index c7e7c454..5e20ace8 100644 --- a/server-core/src/itertools.rs +++ b/server-core/src/itertools.rs @@ -17,17 +17,20 @@ mod size_hint { #[derive(Clone, Debug)] pub struct CoalesceCore - where I: Iterator +where + I: Iterator, { iter: I, last: Option, } impl CoalesceCore - where I: Iterator +where + I: Iterator, { fn next_with(&mut self, mut f: F) -> Option - where F: FnMut(I::Item, I::Item) -> Result + where + F: FnMut(I::Item, I::Item) -> Result, { // this fuses the iterator let mut last = match self.last.take() { @@ -48,8 +51,7 @@ impl CoalesceCore } fn size_hint(&self) -> (usize, Option) { - let (low, hi) = size_hint::add_scalar(self.iter.size_hint(), - self.last.is_some() as usize); + let (low, hi) = size_hint::add_scalar(self.iter.size_hint(), self.last.is_some() as usize); ((low > 0) as usize, hi) } } @@ -59,7 +61,8 @@ impl CoalesceCore /// See [`.coalesce()`](../trait.Itertools.html#method.coalesce) for more information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct Coalesce - where I: Iterator +where + I: Iterator, { iter: CoalesceCore, f: F, @@ -67,7 +70,8 @@ pub struct Coalesce /// Create a new `Coalesce`. pub fn coalesce(mut iter: I, f: F) -> Coalesce - where I: Iterator +where + I: Iterator, { Coalesce { iter: CoalesceCore { @@ -79,8 +83,9 @@ pub fn coalesce(mut iter: I, f: F) -> Coalesce } impl Iterator for Coalesce - where I: Iterator, - F: FnMut(I::Item, I::Item) -> Result +where + I: Iterator, + F: FnMut(I::Item, I::Item) -> Result, { type Item = I::Item; @@ -93,7 +98,7 @@ impl Iterator for Coalesce } } -pub trait Itertools : Iterator { +pub trait Itertools: Iterator { /// Return an iterator adaptor that uses the passed-in closure to /// optionally merge together consecutive elements. /// @@ -109,12 +114,12 @@ pub trait Itertools : Iterator { /// /// This iterator is *fused*. fn coalesce(self, f: F) -> Coalesce - where Self: Sized, - F: FnMut(Self::Item, Self::Item) - -> Result + where + Self: Sized, + F: FnMut(Self::Item, Self::Item) -> Result, { coalesce(self, f) } } -impl Itertools for T where T: Iterator { } +impl Itertools for T where T: Iterator {} diff --git a/server-core/src/lib.rs b/server-core/src/lib.rs index b7f4b7e8..8871254c 100644 --- a/server-core/src/lib.rs +++ b/server-core/src/lib.rs @@ -3,126 +3,103 @@ extern crate log; #[macro_use] extern crate serde_derive; +use actix_web::{ + body::{BodyStream, BoxBody}, + web, App, HttpRequest, HttpResponse, Result, +}; + +use std::borrow::Cow; +use std::cmp::{max, min}; use std::collections::BTreeMap; -use std::fs::File; use std::error::Error; -use std::sync::Arc; use std::fmt::{self, Write}; -use std::thread; +use std::fs::File; use std::io; -use std::borrow::Cow; -use std::cmp::{min, max}; +use std::num::NonZeroUsize; use std::path::PathBuf; +use std::sync::Arc; +use std::thread; use std::time::Instant; -use actix_web::{ - body::{ - Body, - BodyStream - }, - web, - App, - HttpRequest, - HttpResponse, - Responder, - Result -}; - use ahash::AHashMap as HashMap; -use actix_web::error::{ErrorNotFound, ErrorBadRequest, ErrorInternalServerError}; -use actix_web::error::Error as ActixWebError; use actix_cors::Cors; -use futures::Stream; -use serde::Serialize; +use actix_web::error::Error as ActixWebError; +use actix_web::error::{ErrorBadRequest, ErrorNotFound}; + use itertools::Itertools; use lru::LruCache; use parking_lot::Mutex; use rayon::prelude::*; +use serde::Serialize; use cli_core::{ - Loader, - Data, - DataId, - BacktraceId, - OperationId, - Frame, - Allocation, - AllocationId, - Tree, - NodeId, - FrameId, - MalloptKind, - VecVec, - CountAndSize, - Map, - MapId, - RegionFlags, - export_as_replay, - export_as_heaptrack, - export_as_flamegraph, - export_as_flamegraph_pl, - table_to_string + export_as_flamegraph, export_as_flamegraph_pl, export_as_heaptrack, export_as_replay, + table_to_string, Allocation, AllocationId, BacktraceId, CountAndSize, Data, DataId, Frame, + FrameId, Loader, MalloptKind, Map, MapId, NodeId, OperationId, RegionFlags, Tree, VecVec, }; use common::Timestamp; +mod byte_channel; +mod filter; mod itertools; mod protocol; mod streaming_channel; -mod byte_channel; mod streaming_serializer; -mod filter; use crate::byte_channel::byte_channel; +use crate::filter::{ + prepare_allocation_filter, prepare_map_filter, prepare_raw_allocation_filter, + prepare_raw_map_filter, AllocationFilter, PrepareFilterError, +}; use crate::streaming_serializer::StreamingSerializer; -use crate::filter::{AllocationFilter, PrepareFilterError, prepare_allocation_filter, prepare_raw_allocation_filter, prepare_map_filter, prepare_raw_map_filter}; struct AllocationGroups { - allocations_by_backtrace: VecVec< BacktraceId, AllocationId > + allocations_by_backtrace: VecVec, } impl AllocationGroups { - fn new< 'a, T: ParallelIterator< Item = (AllocationId, &'a Allocation) > >( iter: T ) -> Self { + fn new<'a, T: ParallelIterator>(iter: T) -> Self { let grouped = iter - .fold_with( - HashMap::new(), - |mut grouped, (id, allocation)| { - grouped.entry( allocation.backtrace ).or_insert_with( Vec::new ).push( id ); - grouped - } - ) + .fold_with(HashMap::new(), |mut grouped, (id, allocation)| { + grouped + .entry(allocation.backtrace) + .or_insert_with(Vec::new) + .push(id); + grouped + }) .reduce( || HashMap::new(), |mut a, mut b| { if b.len() > a.len() { - std::mem::swap( &mut a, &mut b ); + std::mem::swap(&mut a, &mut b); } for (backtrace, ids) in b { - a.entry( backtrace ).or_insert_with( Vec::new ).extend( ids ); + a.entry(backtrace).or_insert_with(Vec::new).extend(ids); } a - } + }, ); - let mut grouped: Vec< (BacktraceId, Vec< AllocationId >) > = grouped.into_iter().collect(); - grouped.par_sort_by_key( |&(backtrace_id, _)| backtrace_id ); + let mut grouped: Vec<(BacktraceId, Vec)> = grouped.into_iter().collect(); + grouped.par_sort_by_key(|&(backtrace_id, _)| backtrace_id); let mut allocations = VecVec::new(); for (backtrace_id, allocation_ids) in grouped { - allocations.insert( backtrace_id, allocation_ids ); + allocations.insert(backtrace_id, allocation_ids); } let groups = AllocationGroups { - allocations_by_backtrace: allocations + allocations_by_backtrace: allocations, }; groups } - fn len( &self ) -> usize { + fn len(&self) -> usize { self.allocations_by_backtrace.len() } } @@ -133,7 +110,7 @@ struct AllocationGroupsKey { filter: protocol::AllocFilter, custom_filter: protocol::CustomFilter, sort_by: protocol::AllocGroupsSortBy, - order: protocol::Order + order: protocol::Order, } #[derive(Clone)] @@ -141,48 +118,48 @@ struct GeneratedFile { timestamp: Instant, hash: String, mime: &'static str, - data: Arc< Vec< u8 > > + data: Arc>, } #[derive(Default)] struct GeneratedFilesCollection { - by_hash: HashMap< String, GeneratedFile >, - total_size: usize + by_hash: HashMap, + total_size: usize, } impl GeneratedFilesCollection { - fn purge_old_if_too_big( &mut self ) { + fn purge_old_if_too_big(&mut self) { if self.total_size < 32 * 1024 * 1024 { return; } - let mut list: Vec< _ > = self.by_hash.values().cloned().collect(); - list.sort_by_key( |entry| entry.timestamp ); + let mut list: Vec<_> = self.by_hash.values().cloned().collect(); + list.sort_by_key(|entry| entry.timestamp); list.reverse(); - while let Some( entry ) = list.pop() { + while let Some(entry) = list.pop() { if self.total_size <= 16 * 1024 * 1024 { break; } self.total_size -= entry.data.len(); - self.by_hash.remove( &entry.hash ); + self.by_hash.remove(&entry.hash); } } - fn add_file( &mut self, entry: GeneratedFile ) { - if !self.by_hash.contains_key( &entry.hash ) { + fn add_file(&mut self, entry: GeneratedFile) { + if !self.by_hash.contains_key(&entry.hash) { self.total_size += entry.data.len(); - self.by_hash.insert( entry.hash.clone(), entry ); + self.by_hash.insert(entry.hash.clone(), entry); } } } struct State { - data: HashMap< DataId, Arc< Data > >, - data_ids: Vec< DataId >, - allocation_group_cache: Mutex< LruCache< AllocationGroupsKey, Arc< AllocationGroups > > >, - generated_files: Mutex< GeneratedFilesCollection > + data: HashMap>, + data_ids: Vec, + allocation_group_cache: Mutex>>, + generated_files: Mutex, } impl State { @@ -190,49 +167,54 @@ impl State { State { data: HashMap::new(), data_ids: Vec::new(), - allocation_group_cache: Mutex::new( LruCache::new( 4 ) ), + allocation_group_cache: Mutex::new(LruCache::new(NonZeroUsize::new(4).unwrap())), generated_files: Default::default(), } } - fn add_data( &mut self, data: Data ) { - if self.data.contains_key( &data.id() ) { + fn add_data(&mut self, data: Data) { + if self.data.contains_key(&data.id()) { return; } - self.data_ids.push( data.id() ); - self.data.insert( data.id(), Arc::new( data ) ); + self.data_ids.push(data.id()); + self.data.insert(data.id(), Arc::new(data)); } - fn last_id( &self ) -> Option< DataId > { + fn last_id(&self) -> Option { self.data_ids.last().cloned() } - fn generate_graphs( &self, data: &Data, args: cli_core::script::EngineArgs, code: &str ) -> Vec< String > { - let env = Arc::new( Mutex::new( cli_core::script::VirtualEnvironment::new() ) ); - let engine = cli_core::script::Engine::new( env.clone(), args ); - engine.run( &code ).unwrap(); + fn generate_graphs( + &self, + data: &Data, + args: cli_core::script::EngineArgs, + code: &str, + ) -> Vec { + let env = Arc::new(Mutex::new(cli_core::script::VirtualEnvironment::new())); + let engine = cli_core::script::Engine::new(env.clone(), args); + engine.run(&code).unwrap(); let mut urls = Vec::new(); - let files = std::mem::take( &mut env.lock().output ); + let files = std::mem::take(&mut env.lock().output); for file in files { match file { cli_core::script::ScriptOutputKind::Image { path, data: bytes } => { - let hash = format!( "{:x}", md5::compute( &*bytes ) ); - let basename = path[ path.rfind( "/" ).unwrap() + 1.. ].to_owned(); - let url = format!( "/data/{}/script_files/{}/{}", data.id(), hash, basename ); + let hash = format!("{:x}", md5::compute(&*bytes)); + let basename = path[path.rfind("/").unwrap() + 1..].to_owned(); + let url = format!("/data/{}/script_files/{}/{}", data.id(), hash, basename); let entry = GeneratedFile { timestamp: Instant::now(), hash, mime: "image/svg+xml", - data: bytes + data: bytes, }; let mut generated = self.generated_files.lock(); generated.purge_old_if_too_big(); - generated.add_file( entry ); + generated.add_file(entry); - urls.push( url ); - }, + urls.push(url); + } _ => {} } } @@ -241,90 +223,95 @@ impl State { } } -type StateRef = Arc< State >; +type StateRef = Arc; trait StateGetter { - fn state( &self ) -> &StateRef; + fn state(&self) -> &StateRef; } impl StateGetter for HttpRequest { - fn state( &self ) -> &StateRef { - self.app_data::< StateRef >().unwrap() + fn state(&self) -> &StateRef { + self.app_data::>().unwrap() } } -fn query< 'a, T: serde::Deserialize< 'a > >( req: &'a HttpRequest ) -> Result< T > { - serde_urlencoded::from_str::( req.query_string() ) - .map_err( |e| e.into() ) +fn query<'a, T: serde::Deserialize<'a>>(req: &'a HttpRequest) -> Result { + serde_urlencoded::from_str::(req.query_string()).map_err(|e| e.into()) } -fn get_data_id( req: &HttpRequest ) -> Result< DataId > { - let id = req.match_info().get( "id" ).unwrap(); +fn get_data_id(req: &HttpRequest) -> Result { + let id = req.match_info().get("id").unwrap(); if id == "last" { - return req.state().last_id().ok_or( ErrorNotFound( "data not found" ) ); + return req.state().last_id().ok_or(ErrorNotFound("data not found")); } - let id: DataId = id.parse().map_err( |_| ErrorNotFound( "data not found" ) )?; - if !req.state().data.contains_key( &id ) { - return Err( ErrorNotFound( "data not found" ) ); + let id: DataId = id.parse().map_err(|_| ErrorNotFound("data not found"))?; + if !req.state().data.contains_key(&id) { + return Err(ErrorNotFound("data not found")); } - Ok( id ) + Ok(id) } -fn get_data( req: &HttpRequest ) -> Result< &Arc< Data > > { - let id = get_data_id( req )?; - req.state().data.get( &id ).ok_or_else( || ErrorNotFound( "data not found" ) ) +fn get_data(req: &HttpRequest) -> Result<&Arc> { + let id = get_data_id(req)?; + req.state() + .data + .get(&id) + .ok_or_else(|| ErrorNotFound("data not found")) } -impl From< PrepareFilterError > for ActixWebError { - fn from( error: PrepareFilterError ) -> Self { +impl From for ActixWebError { + fn from(error: PrepareFilterError) -> Self { match error { - PrepareFilterError::InvalidRegex( field, inner_err ) => { - ErrorBadRequest( format!( "invalid '{}': {}", field, inner_err ) ) - }, - PrepareFilterError::InvalidCustomFilter( message ) => { - ErrorBadRequest( format!( "failed to evaluate custom filter: {}", message ) ) + PrepareFilterError::InvalidRegex(field, inner_err) => { + ErrorBadRequest(format!("invalid '{}': {}", field, inner_err)) + } + PrepareFilterError::InvalidCustomFilter(message) => { + ErrorBadRequest(format!("failed to evaluate custom filter: {}", message)) } } } } -fn async_data_handler< F: FnOnce( Arc< Data >, byte_channel::ByteSender ) + Send + 'static >( req: &HttpRequest, callback: F ) -> Result< Body > { +fn async_data_handler, byte_channel::ByteSender) + Send + 'static>( + req: &HttpRequest, + callback: F, +) -> Result { let (tx, rx) = byte_channel(); - let rx = rx.map_err( |_| ErrorInternalServerError( "internal error" ) ); - let rx = BodyStream::new( rx ); - let body = Body::Message( Box::new( rx ) ); + // let rx = rx.map_err(|_| ErrorInternalServerError("internal error")); + let rx = BodyStream::new(rx); + let body = BoxBody::new(rx); - let data_id = get_data_id( &req )?; + let data_id = get_data_id(&req)?; let state = req.state().clone(); - thread::spawn( move || { - let data = match state.data.get( &data_id ) { - Some( data ) => data, - None => return + thread::spawn(move || { + let data = match state.data.get(&data_id) { + Some(data) => data, + None => return, }; - callback( data.clone(), tx ); + callback(data.clone(), tx); }); - Ok( body ) + Ok(body) } -fn strip_template( input: &str ) -> String { +fn strip_template(input: &str) -> String { let mut out = String::new(); let mut buffered = String::new(); let mut depth = 0; for ch in input.chars() { if ch == '<' { - if out.ends_with( "operator" ) { - out.push( ch ); - continue + if out.ends_with("operator") { + out.push(ch); + continue; } if depth == 0 { buffered.clear(); - out.push( ch ); + out.push(ch); } else { - buffered.push( ch ); + buffered.push(ch); } depth += 1; @@ -335,96 +322,121 @@ fn strip_template( input: &str ) -> String { if ch == '>' { depth -= 1; if depth == 0 { - out.push_str( "..." ); - out.push( ch ); + out.push_str("..."); + out.push(ch); buffered.clear(); } continue; } - buffered.push( ch ); + buffered.push(ch); continue; } - out.push( ch ); + out.push(ch); } - out.push_str( &buffered ); + out.push_str(&buffered); out } -fn get_frame< 'a >( data: &'a Data, format: &protocol::BacktraceFormat, frame: &Frame ) -> protocol::Frame< 'a > { - let mut function = frame.function().map( |id| Cow::Borrowed( data.interner().resolve( id ).unwrap() ) ); - if format.strip_template_args.unwrap_or( false ) { - function = function.map( |function| strip_template( &function ).into() ); +fn get_frame<'a>( + data: &'a Data, + format: &protocol::BacktraceFormat, + frame: &Frame, +) -> protocol::Frame<'a> { + let mut function = frame + .function() + .map(|id| Cow::Borrowed(data.interner().resolve(id).unwrap())); + if format.strip_template_args.unwrap_or(false) { + function = function.map(|function| strip_template(&function).into()); } protocol::Frame { address: frame.address().raw(), - address_s: format!( "{:016X}", frame.address().raw() ), + address_s: format!("{:016X}", frame.address().raw()), count: frame.count(), - library: frame.library().map( |id| data.interner().resolve( id ).unwrap() ), + library: frame + .library() + .map(|id| data.interner().resolve(id).unwrap()), function, - raw_function: frame.raw_function().map( |id| data.interner().resolve( id ).unwrap() ), - source: frame.source().map( |id| data.interner().resolve( id ).unwrap() ), + raw_function: frame + .raw_function() + .map(|id| data.interner().resolve(id).unwrap()), + source: frame + .source() + .map(|id| data.interner().resolve(id).unwrap()), line: frame.line(), column: frame.column(), - is_inline: frame.is_inline() + is_inline: frame.is_inline(), } } impl protocol::ResponseMetadata { - fn new( data: &Data ) -> Self { + fn new(data: &Data) -> Self { protocol::ResponseMetadata { - id: format!( "{}", data.id() ), + id: format!("{}", data.id()), executable: data.executable().to_owned(), - cmdline: data.cmdline().into_iter().map( |arg| { - if arg.contains( " " ) { - format!( "\"{}\"", arg ) - } else { - arg - } - }).collect::< Vec< _ > >().join( " " ), + cmdline: data + .cmdline() + .into_iter() + .map(|arg| { + if arg.contains(" ") { + format!("\"{}\"", arg) + } else { + arg + } + }) + .collect::>() + .join(" "), architecture: data.architecture().to_owned(), final_allocated: data.total_allocated() - data.total_freed(), final_allocated_count: data.total_allocated_count() - data.total_freed_count(), runtime: (data.last_timestamp() - data.initial_timestamp()).into(), unique_backtrace_count: data.unique_backtrace_count() as u64, maximum_backtrace_depth: data.maximum_backtrace_depth(), - timestamp: data.initial_timestamp().into() + timestamp: data.initial_timestamp().into(), } } } -fn handler_list( req: HttpRequest ) -> HttpResponse { - let list: Vec< _ > = req.state().data.values().map( |data| { - protocol::ResponseMetadata::new( data ) - }).collect(); +async fn handler_list(req: HttpRequest) -> HttpResponse { + let list: Vec<_> = req + .state() + .data + .values() + .map(|data| protocol::ResponseMetadata::new(data)) + .collect(); - HttpResponse::Ok().json( list ) + HttpResponse::Ok().json(list) } -fn build_timeline( data: &Data, ops: &[OperationId] ) -> protocol::ResponseTimeline { - let timeline = cli_core::build_allocation_timeline( data, data.initial_timestamp(), data.last_timestamp(), ops ); +fn build_timeline(data: &Data, ops: &[OperationId]) -> protocol::ResponseTimeline { + let timeline = cli_core::build_allocation_timeline( + data, + data.initial_timestamp(), + data.last_timestamp(), + ops, + ); - let mut xs = Vec::with_capacity( timeline.len() ); - let mut size_delta = Vec::with_capacity( timeline.len() ); - let mut count_delta = Vec::with_capacity( timeline.len() ); - let mut allocated_size = Vec::with_capacity( timeline.len() ); - let mut allocated_count = Vec::with_capacity( timeline.len() ); - let mut allocations = Vec::with_capacity( timeline.len() ); - let mut deallocations = Vec::with_capacity( timeline.len() ); + let mut xs = Vec::with_capacity(timeline.len()); + let mut size_delta = Vec::with_capacity(timeline.len()); + let mut count_delta = Vec::with_capacity(timeline.len()); + let mut allocated_size = Vec::with_capacity(timeline.len()); + let mut allocated_count = Vec::with_capacity(timeline.len()); + let mut allocations = Vec::with_capacity(timeline.len()); + let mut deallocations = Vec::with_capacity(timeline.len()); let mut last_size = 0; let mut last_count = 0; for point in timeline { - xs.push( point.timestamp / 1000 ); - size_delta.push( point.memory_usage as i64 - last_size ); - count_delta.push( point.allocations as i64 - last_count ); - allocated_size.push( point.memory_usage as u64 ); - allocated_count.push( point.allocations as u64 ); - allocations.push( point.positive_change.allocations as u32 ); - deallocations.push( point.negative_change.allocations as u32 ); + xs.push(point.timestamp / 1000); + size_delta.push(point.memory_usage as i64 - last_size); + count_delta.push(point.allocations as i64 - last_count); + allocated_size.push(point.memory_usage as u64); + allocated_count.push(point.allocations as u64); + allocations.push(point.positive_change.allocations as u32); + deallocations.push(point.negative_change.allocations as u32); last_size = point.memory_usage as i64; last_count = point.allocations as i64; @@ -437,66 +449,76 @@ fn build_timeline( data: &Data, ops: &[OperationId] ) -> protocol::ResponseTimel allocated_size, allocated_count, allocations, - deallocations + deallocations, } } -fn handler_timeline( req: HttpRequest ) -> Result< HttpResponse > { - let data = get_data( &req )?; - let timeline = build_timeline( &data, data.operation_ids() ); - Ok( HttpResponse::Ok().json( timeline ) ) +async fn handler_timeline(req: HttpRequest) -> Result { + let data = get_data(&req)?; + let timeline = build_timeline(&data, data.operation_ids()); + Ok(HttpResponse::Ok().json(timeline)) } -fn handler_timeline_leaked( req: HttpRequest ) -> Result< HttpResponse > { - let data = get_data( &req )?; - let ops: Vec< _ > = data.operation_ids().par_iter().flat_map( |op| { - let allocation = data.get_allocation( op.id() ); - if allocation.deallocation.is_some() { - None - } else { - Some( OperationId::new_allocation( op.id() ) ) - } - }).collect(); +async fn handler_timeline_leaked(req: HttpRequest) -> Result { + let data = get_data(&req)?; + let ops: Vec<_> = data + .operation_ids() + .par_iter() + .flat_map(|op| { + let allocation = data.get_allocation(op.id()); + if allocation.deallocation.is_some() { + None + } else { + Some(OperationId::new_allocation(op.id())) + } + }) + .collect(); - let timeline = build_timeline( &data, &ops ); - Ok( HttpResponse::Ok().json( timeline ) ) + let timeline = build_timeline(&data, &ops); + Ok(HttpResponse::Ok().json(timeline)) } -fn handler_timeline_maps( req: HttpRequest ) -> Result< HttpResponse > { - let data = get_data( &req )?; +async fn handler_timeline_maps(req: HttpRequest) -> Result { + let data = get_data(&req)?; let mut ops = Vec::new(); for map in data.maps() { if map.is_from_bytehound() { continue; } - map.emit_ops( &mut ops ); + map.emit_ops(&mut ops); } - ops.par_sort_by_key( |(timestamp, _)| *timestamp ); + ops.par_sort_by_key(|(timestamp, _)| *timestamp); - let timestamp_min = ops.first().map( |(timestamp, _)| *timestamp ).unwrap_or( common::Timestamp::min() ); - let timestamp_max = ops.last().map( |(timestamp, _)| *timestamp ).unwrap_or( common::Timestamp::min() ); + let timestamp_min = ops + .first() + .map(|(timestamp, _)| *timestamp) + .unwrap_or(common::Timestamp::min()); + let timestamp_max = ops + .last() + .map(|(timestamp, _)| *timestamp) + .unwrap_or(common::Timestamp::min()); - let timeline = cli_core::build_map_timeline( timestamp_min, timestamp_max, &ops ); - std::mem::drop( ops ); + let timeline = cli_core::build_map_timeline(timestamp_min, timestamp_max, &ops); + std::mem::drop(ops); - let mut xs = Vec::with_capacity( timeline.len() ); - let mut address_space = Vec::with_capacity( timeline.len() ); - let mut rss = Vec::with_capacity( timeline.len() ); - let mut anonymous = Vec::with_capacity( timeline.len() ); - let mut dirty = Vec::with_capacity( timeline.len() ); - let mut clean = Vec::with_capacity( timeline.len() ); - let mut swap = Vec::with_capacity( timeline.len() ); + let mut xs = Vec::with_capacity(timeline.len()); + let mut address_space = Vec::with_capacity(timeline.len()); + let mut rss = Vec::with_capacity(timeline.len()); + let mut anonymous = Vec::with_capacity(timeline.len()); + let mut dirty = Vec::with_capacity(timeline.len()); + let mut clean = Vec::with_capacity(timeline.len()); + let mut swap = Vec::with_capacity(timeline.len()); for point in timeline { - xs.push( point.timestamp / 1000 ); - address_space.push( point.address_space as i64 ); - rss.push( point.rss() as i64 ); - anonymous.push( point.anonymous as i64 ); - dirty.push( point.dirty() as i64 ); - clean.push( point.clean() as i64 ); - swap.push( point.swap as i64 ); + xs.push(point.timestamp / 1000); + address_space.push(point.address_space as i64); + rss.push(point.rss() as i64); + anonymous.push(point.anonymous as i64); + dirty.push(point.dirty() as i64); + clean.push(point.clean() as i64); + swap.push(point.swap as i64); } let timeline = protocol::ResponseMapTimeline { @@ -506,171 +528,200 @@ fn handler_timeline_maps( req: HttpRequest ) -> Result< HttpResponse > { anonymous, dirty, clean, - swap + swap, }; - Ok( HttpResponse::Ok().json( timeline ) ) + Ok(HttpResponse::Ok().json(timeline)) } -fn prefiltered_allocation_ids< 'a >( +fn prefiltered_allocation_ids<'a>( data: &'a Data, sort_by: protocol::AllocSortBy, - _filter: &AllocationFilter - ) -> &'a [AllocationId] { + _filter: &AllocationFilter, +) -> &'a [AllocationId] { // TODO: Use the filter to narrow down the range. match sort_by { - protocol::AllocSortBy::Timestamp => data.alloc_sorted_by_timestamp( None, None ), - protocol::AllocSortBy::Address => data.alloc_sorted_by_address( None, None ), - protocol::AllocSortBy::Size => data.alloc_sorted_by_size( None, None ) + protocol::AllocSortBy::Timestamp => data.alloc_sorted_by_timestamp(None, None), + protocol::AllocSortBy::Address => data.alloc_sorted_by_address(None, None), + protocol::AllocSortBy::Size => data.alloc_sorted_by_size(None, None), } } -fn allocations_iter< 'a >( +fn allocations_iter<'a>( data: &'a Data, array: &'a [AllocationId], order: protocol::Order, - filter: AllocationFilter -) -> Box< dyn DoubleEndedIterator< Item = (AllocationId, &'a Allocation) > + 'a > { + filter: AllocationFilter, +) -> Box + 'a> { match order { - protocol::Order::Asc => Box::new( array.iter() - .map( move |&id| (id, data.get_allocation( id )) ) - .filter( move |(id, allocation)| filter.try_match( data, *id, allocation ) ) + protocol::Order::Asc => Box::new( + array + .iter() + .map(move |&id| (id, data.get_allocation(id))) + .filter(move |(id, allocation)| filter.try_match(data, *id, allocation)), + ), + protocol::Order::Dsc => Box::new( + array + .iter() + .rev() + .map(move |&id| (id, data.get_allocation(id))) + .filter(move |(id, allocation)| filter.try_match(data, *id, allocation)), ), - protocol::Order::Dsc => Box::new( array.iter().rev() - .map( move |&id| (id, data.get_allocation( id )) ) - .filter( move |(id, allocation)| filter.try_match( data, *id, allocation ) ) - ) } } -fn timestamp_to_fraction( data: &Data, timestamp: Timestamp ) -> f32 { +fn timestamp_to_fraction(data: &Data, timestamp: Timestamp) -> f32 { let relative = timestamp - data.initial_timestamp(); let range = data.last_timestamp() - data.initial_timestamp(); (relative.as_usecs() as f64 / range.as_usecs() as f64) as f32 } -fn get_allocations< 'a >( - data: &'a Arc< Data >, +fn get_allocations<'a>( + data: &'a Arc, backtrace_format: protocol::BacktraceFormat, params: protocol::RequestAllocations, - filter: crate::filter::AllocationFilter -) -> protocol::ResponseAllocations< impl Serialize + 'a > { - let remaining = params.count.unwrap_or( -1_i32 as _ ) as usize; - let skip = params.skip.unwrap_or( 0 ) as usize; - let sort_by = params.sort_by.unwrap_or( protocol::AllocSortBy::Timestamp ); - let order = params.order.unwrap_or( protocol::Order::Asc ); - - let allocation_ids = prefiltered_allocation_ids( data, sort_by, &filter ); - let total_count = - allocation_ids + filter: crate::filter::AllocationFilter, +) -> protocol::ResponseAllocations { + let remaining = params.count.unwrap_or(-1_i32 as _) as usize; + let skip = params.skip.unwrap_or(0) as usize; + let sort_by = params.sort_by.unwrap_or(protocol::AllocSortBy::Timestamp); + let order = params.order.unwrap_or(protocol::Order::Asc); + + let allocation_ids = prefiltered_allocation_ids(data, sort_by, &filter); + let total_count = allocation_ids .par_iter() - .filter( |&&id| filter.try_match( data, id, data.get_allocation( id ) ) ) + .filter(|&&id| filter.try_match(data, id, data.get_allocation(id))) .count() as u64; let allocations = move || { let backtrace_format = backtrace_format.clone(); let filter = filter.clone(); - allocations_iter( data, allocation_ids, order, filter ) - .skip( skip ) - .take( remaining ) - .map( move |(allocation_id, allocation)| { - let backtrace = data.get_backtrace( allocation.backtrace ).map( |(_, frame)| get_frame( data, &backtrace_format, frame ) ).collect(); - let chain = data.get_chain_by_any_allocation( allocation_id ); + allocations_iter(data, allocation_ids, order, filter) + .skip(skip) + .take(remaining) + .map(move |(allocation_id, allocation)| { + let backtrace = data + .get_backtrace(allocation.backtrace) + .map(|(_, frame)| get_frame(data, &backtrace_format, frame)) + .collect(); + let chain = data.get_chain_by_any_allocation(allocation_id); protocol::Allocation { id: allocation_id.raw(), address: allocation.pointer, - address_s: format!( "{:016X}", allocation.pointer ), + address_s: format!("{:016X}", allocation.pointer), timestamp: allocation.timestamp.into(), timestamp_relative: (allocation.timestamp - data.initial_timestamp()).into(), - timestamp_relative_p: timestamp_to_fraction( data, allocation.timestamp ), + timestamp_relative_p: timestamp_to_fraction(data, allocation.timestamp), thread: allocation.thread, size: allocation.size, backtrace_id: allocation.backtrace.raw(), - deallocation: allocation.deallocation.as_ref().map( |deallocation| { + deallocation: allocation.deallocation.as_ref().map(|deallocation| { protocol::Deallocation { timestamp: deallocation.timestamp.into(), thread: deallocation.thread, - backtrace_id: deallocation.backtrace.map( |id| id.raw() ), - backtrace: deallocation.backtrace.map( |backtrace_id| { - data.get_backtrace( backtrace_id ).map( |(_, frame)| get_frame( data, &backtrace_format, frame ) ).collect() - }) + backtrace_id: deallocation.backtrace.map(|id| id.raw()), + backtrace: deallocation.backtrace.map(|backtrace_id| { + data.get_backtrace(backtrace_id) + .map(|(_, frame)| get_frame(data, &backtrace_format, frame)) + .collect() + }), } }), chain_deallocation: if chain.last == allocation_id { None } else { - data.get_allocation( chain.last ).deallocation.as_ref().map( |deallocation| { - protocol::Deallocation { + data.get_allocation(chain.last) + .deallocation + .as_ref() + .map(|deallocation| protocol::Deallocation { timestamp: deallocation.timestamp.into(), thread: deallocation.thread, - backtrace_id: deallocation.backtrace.map( |id| id.raw() ), - backtrace: deallocation.backtrace.map( |backtrace_id| { - data.get_backtrace( backtrace_id ).map( |(_, frame)| get_frame( data, &backtrace_format, frame ) ).collect() - }) - } - }) + backtrace_id: deallocation.backtrace.map(|id| id.raw()), + backtrace: deallocation.backtrace.map(|backtrace_id| { + data.get_backtrace(backtrace_id) + .map(|(_, frame)| get_frame(data, &backtrace_format, frame)) + .collect() + }), + }) }, backtrace, in_main_arena: !allocation.in_non_main_arena(), is_mmaped: allocation.is_mmaped(), is_jemalloc: allocation.is_jemalloc(), extra_space: allocation.extra_usable_space, - chain_lifetime: chain.lifetime( data ).map( |lifetime| lifetime.into() ), + chain_lifetime: chain.lifetime(data).map(|lifetime| lifetime.into()), position_in_chain: allocation.position_in_chain, - chain_length: chain.length + chain_length: chain.length, } }) }; protocol::ResponseAllocations { - allocations: StreamingSerializer::new( allocations ), - total_count + allocations: StreamingSerializer::new(allocations), + total_count, } } -fn handler_allocations( req: HttpRequest ) -> Result< HttpResponse > { - let data = get_data( &req )?; - let params: protocol::RequestAllocations = query( &req )?; - let filter: protocol::AllocFilter = query( &req )?; - let custom_filter: protocol::CustomFilter = query( &req )?; - let filter = prepare_allocation_filter( data, &filter, &custom_filter )?; - let backtrace_format: protocol::BacktraceFormat = query( &req )?; - - let body = async_data_handler( &req, move |data, tx| { - let response = get_allocations( &data, backtrace_format, params, filter ); - let _ = serde_json::to_writer( tx, &response ); +async fn handler_allocations(req: HttpRequest) -> Result { + let data = get_data(&req)?; + let params: protocol::RequestAllocations = query(&req)?; + let filter: protocol::AllocFilter = query(&req)?; + let custom_filter: protocol::CustomFilter = query(&req)?; + let filter = prepare_allocation_filter(data, &filter, &custom_filter)?; + let backtrace_format: protocol::BacktraceFormat = query(&req)?; + + let body = async_data_handler(&req, move |data, tx| { + let response = get_allocations(&data, backtrace_format, params, filter); + let _ = serde_json::to_writer(tx, &response); })?; - Ok( HttpResponse::Ok().content_type( "application/json" ).body( body ) ) + Ok(HttpResponse::Ok() + .content_type("application/json") + .body(body)) } -fn get_regions< 'a >( data: &'a Data, backtrace_format: &protocol::BacktraceFormat, map: &'a Map ) -> Vec< protocol::MapRegion< 'a > > { - map.regions.iter().map( |region| { - protocol::MapRegion { +fn get_regions<'a>( + data: &'a Data, + backtrace_format: &protocol::BacktraceFormat, + map: &'a Map, +) -> Vec> { + map.regions + .iter() + .map(|region| protocol::MapRegion { address: region.pointer, - address_s: format!( "{:016X}", region.pointer ), + address_s: format!("{:016X}", region.pointer), timestamp: region.timestamp.into(), timestamp_relative: (region.timestamp - data.initial_timestamp()).into(), - timestamp_relative_p: timestamp_to_fraction( data, region.timestamp ), + timestamp_relative_p: timestamp_to_fraction(data, region.timestamp), size: region.size, - deallocation: region.deallocation.as_ref().map( |deallocation| { + deallocation: region.deallocation.as_ref().map(|deallocation| { protocol::MapRegionDeallocation { timestamp: deallocation.timestamp.into(), - sources: deallocation.sources.iter().map( |source| { - protocol::MapRegionDeallocationSource { + sources: deallocation + .sources + .iter() + .map(|source| protocol::MapRegionDeallocationSource { address: source.address, length: source.length, source: protocol::MapSource { timestamp: source.source.timestamp.into(), - timestamp_relative: (source.source.timestamp - data.initial_timestamp()).into(), - timestamp_relative_p: timestamp_to_fraction( data, source.source.timestamp ), + timestamp_relative: (source.source.timestamp + - data.initial_timestamp()) + .into(), + timestamp_relative_p: timestamp_to_fraction( + data, + source.source.timestamp, + ), thread: source.source.thread, backtrace_id: source.source.backtrace.raw(), - backtrace: data.get_backtrace( source.source.backtrace ).map( |(_, frame)| get_frame( data, backtrace_format, frame ) ).collect() - } - } - }).collect() + backtrace: data + .get_backtrace(source.source.backtrace) + .map(|(_, frame)| get_frame(data, backtrace_format, frame)) + .collect(), + }, + }) + .collect(), } }), file_offset: region.file_offset, @@ -678,81 +729,82 @@ fn get_regions< 'a >( data: &'a Data, backtrace_format: &protocol::BacktraceForm major: region.major, minor: region.minor, name: (&*region.name).into(), - is_readable: region.flags.contains( RegionFlags::READABLE ), - is_writable: region.flags.contains( RegionFlags::WRITABLE ), - is_executable: region.flags.contains( RegionFlags::EXECUTABLE ), - is_shared: region.flags.contains( RegionFlags::SHARED ), - } - }).collect() + is_readable: region.flags.contains(RegionFlags::READABLE), + is_writable: region.flags.contains(RegionFlags::WRITABLE), + is_executable: region.flags.contains(RegionFlags::EXECUTABLE), + is_shared: region.flags.contains(RegionFlags::SHARED), + }) + .collect() } -fn get_maps< 'a >( - state: Arc< State >, - data: &'a Arc< Data >, +fn get_maps<'a>( + state: Arc, + data: &'a Arc, backtrace_format: protocol::BacktraceFormat, params: protocol::RequestMaps, - filter: crate::filter::MapFilter -) -> protocol::ResponseMaps< impl Serialize + 'a > { - let remaining = params.count.unwrap_or( -1_i32 as _ ) as usize; - let skip = params.skip.unwrap_or( 0 ) as usize; - let sort_by = params.sort_by.unwrap_or( protocol::MapsSortBy::Timestamp ); - let order = params.order.unwrap_or( protocol::Order::Asc ); - let generate_graphs = params.generate_graphs.unwrap_or( false ); + filter: crate::filter::MapFilter, +) -> protocol::ResponseMaps { + let remaining = params.count.unwrap_or(-1_i32 as _) as usize; + let skip = params.skip.unwrap_or(0) as usize; + let sort_by = params.sort_by.unwrap_or(protocol::MapsSortBy::Timestamp); + let order = params.order.unwrap_or(protocol::Order::Asc); + let generate_graphs = params.generate_graphs.unwrap_or(false); let mut list = Vec::new(); - if let Some( id ) = params.id { - let id = MapId( id as u64 ); - if data.contains_map( id ) { - list.push( id ); + if let Some(id) = params.id { + let id = MapId(id as u64); + if data.contains_map(id) { + list.push(id); } } else { - list = data.maps().par_iter().enumerate() - .map( |(index, map)| { - let id = MapId( index as u64 ); + list = data + .maps() + .par_iter() + .enumerate() + .map(|(index, map)| { + let id = MapId(index as u64); (id, map) }) - .filter( |(id, map)| { - filter.try_match( data, *id, map ) - }) - .map( |(id, _)| id ) + .filter(|(id, map)| filter.try_match(data, *id, map)) + .map(|(id, _)| id) .collect(); } let cmp = match sort_by { protocol::MapsSortBy::Timestamp => { if order == protocol::Order::Asc { - |a: &Map, b: &Map| a.timestamp.cmp( &b.timestamp ) + |a: &Map, b: &Map| a.timestamp.cmp(&b.timestamp) } else { - |a: &Map, b: &Map| b.timestamp.cmp( &a.timestamp ) + |a: &Map, b: &Map| b.timestamp.cmp(&a.timestamp) } - }, + } protocol::MapsSortBy::Address => { if order == protocol::Order::Asc { - |a: &Map, b: &Map| a.pointer.cmp( &b.pointer ) + |a: &Map, b: &Map| a.pointer.cmp(&b.pointer) } else { - |a: &Map, b: &Map| b.pointer.cmp( &a.pointer ) + |a: &Map, b: &Map| b.pointer.cmp(&a.pointer) } - }, + } protocol::MapsSortBy::Size => { if order == protocol::Order::Asc { - |a: &Map, b: &Map| a.size.cmp( &b.size ) + |a: &Map, b: &Map| a.size.cmp(&b.size) } else { - |a: &Map, b: &Map| b.size.cmp( &a.size ) + |a: &Map, b: &Map| b.size.cmp(&a.size) } - }, + } protocol::MapsSortBy::PeakRss => { if order == protocol::Order::Asc { - |a: &Map, b: &Map| a.peak_rss.cmp( &b.peak_rss ) + |a: &Map, b: &Map| a.peak_rss.cmp(&b.peak_rss) } else { - |a: &Map, b: &Map| b.peak_rss.cmp( &a.peak_rss ) + |a: &Map, b: &Map| b.peak_rss.cmp(&a.peak_rss) } } }; - list.par_sort_by( move |a, b| { - let a = &data.maps()[ a.0 as usize ]; - let b = &data.maps()[ b.0 as usize ]; - cmp( a, b ) + list.par_sort_by(move |a, b| { + let a = &data.maps()[a.0 as usize]; + let b = &data.maps()[b.0 as usize]; + cmp(a, b) }); let total_count = list.len() as u64; @@ -760,139 +812,145 @@ fn get_maps< 'a >( let maps = move || { let backtrace_format = backtrace_format.clone(); - list.into_iter() - .skip( skip ) - .take( remaining ) - .map( move |id| { - let map = &data.maps()[ id.0 as usize ]; - let source = map.source.map( |source| { - protocol::MapSource { - timestamp: source.timestamp.into(), - timestamp_relative: (source.timestamp - data.initial_timestamp()).into(), - timestamp_relative_p: timestamp_to_fraction( data, source.timestamp ), - thread: source.thread, - backtrace_id: source.backtrace.raw(), - backtrace: data.get_backtrace( source.backtrace ).map( |(_, frame)| get_frame( data, &backtrace_format, frame ) ).collect() - } - }); + list.into_iter().skip(skip).take(remaining).map(move |id| { + let map = &data.maps()[id.0 as usize]; + let source = map.source.map(|source| protocol::MapSource { + timestamp: source.timestamp.into(), + timestamp_relative: (source.timestamp - data.initial_timestamp()).into(), + timestamp_relative_p: timestamp_to_fraction(data, source.timestamp), + thread: source.thread, + backtrace_id: source.backtrace.raw(), + backtrace: data + .get_backtrace(source.backtrace) + .map(|(_, frame)| get_frame(data, &backtrace_format, frame)) + .collect(), + }); - let mut graph_preview_url = None; - let mut graph_url = None; + let mut graph_preview_url = None; + let mut graph_url = None; - if generate_graphs { - let code = format!( r#" + if generate_graphs { + let code = format!( + r#" let graph = graph() .add(maps()) .save() .without_axes() .without_legend() .save(); - "# ); - - let args = cli_core::script::EngineArgs { - data: Some( data.clone() ), - map_ids: Some( Arc::new( vec![ id ] ) ), - .. cli_core::script::EngineArgs::default() - }; + "# + ); - let mut urls = state.generate_graphs( &data, args, &code ).into_iter(); - graph_url = urls.next(); - graph_preview_url = urls.next(); - } + let args = cli_core::script::EngineArgs { + data: Some(data.clone()), + map_ids: Some(Arc::new(vec![id])), + ..cli_core::script::EngineArgs::default() + }; - let regions = - if !params.with_regions.unwrap_or( false ) { - None - } else { - Some( get_regions( &data, &backtrace_format, map ) ) - }; + let mut urls = state.generate_graphs(&data, args, &code).into_iter(); + graph_url = urls.next(); + graph_preview_url = urls.next(); + } - let usage_history = - if !params.with_usage_history.unwrap_or( false ) { - None - } else { - Some( map.usage_history.iter().map( |usage| { - protocol::MapUsage { - timestamp: usage.timestamp.into(), - timestamp_relative: (usage.timestamp - data.initial_timestamp()).into(), - timestamp_relative_p: timestamp_to_fraction( data, usage.timestamp ), - address_space: usage.address_space, - anonymous: usage.anonymous, - shared_clean: usage.shared_clean, - shared_dirty: usage.shared_dirty, - private_clean: usage.private_clean, - private_dirty: usage.private_dirty, - swap: usage.swap, - rss: usage.rss(), - } - }).collect() ) - }; + let regions = if !params.with_regions.unwrap_or(false) { + None + } else { + Some(get_regions(&data, &backtrace_format, map)) + }; - protocol::Map { - id: id.0, - - address: map.pointer, - address_s: format!( "{:016X}", map.pointer ), - timestamp: map.timestamp.into(), - timestamp_relative: (map.timestamp - data.initial_timestamp()).into(), - timestamp_relative_p: timestamp_to_fraction( data, map.timestamp ), - size: map.size, - source, - deallocation: map.deallocation.as_ref().map( |deallocation| { - protocol::MapDeallocation { - timestamp: deallocation.timestamp.into(), - source: deallocation.source.map( |source| { - protocol::MapSource { - timestamp: source.timestamp.into(), - timestamp_relative: (source.timestamp - data.initial_timestamp()).into(), - timestamp_relative_p: timestamp_to_fraction( data, source.timestamp ), - thread: source.thread, - backtrace_id: source.backtrace.raw(), - backtrace: data.get_backtrace( source.backtrace ).map( |(_, frame)| get_frame( data, &backtrace_format, frame ) ).collect() - } - }) - } - }), - name: (&*map.name).into(), - is_readable: map.flags.contains( RegionFlags::READABLE ), - is_writable: map.flags.contains( RegionFlags::WRITABLE ), - is_executable: map.flags.contains( RegionFlags::EXECUTABLE ), - is_shared: map.flags.contains( RegionFlags::SHARED ), - - peak_rss: map.peak_rss, - graph_preview_url, - graph_url, - regions, - usage_history - } - }) + let usage_history = if !params.with_usage_history.unwrap_or(false) { + None + } else { + Some( + map.usage_history + .iter() + .map(|usage| protocol::MapUsage { + timestamp: usage.timestamp.into(), + timestamp_relative: (usage.timestamp - data.initial_timestamp()).into(), + timestamp_relative_p: timestamp_to_fraction(data, usage.timestamp), + address_space: usage.address_space, + anonymous: usage.anonymous, + shared_clean: usage.shared_clean, + shared_dirty: usage.shared_dirty, + private_clean: usage.private_clean, + private_dirty: usage.private_dirty, + swap: usage.swap, + rss: usage.rss(), + }) + .collect(), + ) + }; + + protocol::Map { + id: id.0, + + address: map.pointer, + address_s: format!("{:016X}", map.pointer), + timestamp: map.timestamp.into(), + timestamp_relative: (map.timestamp - data.initial_timestamp()).into(), + timestamp_relative_p: timestamp_to_fraction(data, map.timestamp), + size: map.size, + source, + deallocation: map.deallocation.as_ref().map(|deallocation| { + protocol::MapDeallocation { + timestamp: deallocation.timestamp.into(), + source: deallocation.source.map(|source| protocol::MapSource { + timestamp: source.timestamp.into(), + timestamp_relative: (source.timestamp - data.initial_timestamp()) + .into(), + timestamp_relative_p: timestamp_to_fraction(data, source.timestamp), + thread: source.thread, + backtrace_id: source.backtrace.raw(), + backtrace: data + .get_backtrace(source.backtrace) + .map(|(_, frame)| get_frame(data, &backtrace_format, frame)) + .collect(), + }), + } + }), + name: (&*map.name).into(), + is_readable: map.flags.contains(RegionFlags::READABLE), + is_writable: map.flags.contains(RegionFlags::WRITABLE), + is_executable: map.flags.contains(RegionFlags::EXECUTABLE), + is_shared: map.flags.contains(RegionFlags::SHARED), + + peak_rss: map.peak_rss, + graph_preview_url, + graph_url, + regions, + usage_history, + } + }) }; protocol::ResponseMaps { - maps: StreamingSerializer::new( maps ), - total_count + maps: StreamingSerializer::new(maps), + total_count, } } -fn handler_maps( req: HttpRequest ) -> Result< HttpResponse > { - let data = get_data( &req )?; - let params: protocol::RequestMaps = query( &req )?; - let filter: protocol::MapFilter = query( &req )?; - let custom_filter: protocol::CustomFilter = query( &req )?; - let filter = prepare_map_filter( data, &filter, &custom_filter )?; - let backtrace_format: protocol::BacktraceFormat = query( &req )?; +async fn handler_maps(req: HttpRequest) -> Result { + let data = get_data(&req)?; + let params: protocol::RequestMaps = query(&req)?; + let filter: protocol::MapFilter = query(&req)?; + let custom_filter: protocol::CustomFilter = query(&req)?; + let filter = prepare_map_filter(data, &filter, &custom_filter)?; + let backtrace_format: protocol::BacktraceFormat = query(&req)?; let state = req.state().clone(); - let body = async_data_handler( &req, move |data, tx| { - let response = get_maps( state, &data, backtrace_format, params, filter ); - let _ = serde_json::to_writer( tx, &response ); + let body = async_data_handler(&req, move |data, tx| { + let response = get_maps(state, &data, backtrace_format, params, filter); + let _ = serde_json::to_writer(tx, &response); })?; - Ok( HttpResponse::Ok().content_type( "application/json" ).body( body ) ) + Ok(HttpResponse::Ok() + .content_type("application/json") + .body(body)) } -fn get_allocation_group_data< 'a, I >( data: &Data, iter: I ) -> protocol::AllocationGroupData - where I: ParallelIterator< Item = &'a Allocation > +fn get_allocation_group_data<'a, I>(data: &Data, iter: I) -> protocol::AllocationGroupData +where + I: ParallelIterator, { #[derive(Clone)] struct Group { @@ -902,7 +960,7 @@ fn get_allocation_group_data< 'a, I >( data: &Data, iter: I ) -> protocol::Alloc min_timestamp: Timestamp, max_timestamp: Timestamp, leaked_count: u64, - allocated_count: u64 + allocated_count: u64, } impl Default for Group { @@ -914,21 +972,20 @@ fn get_allocation_group_data< 'a, I >( data: &Data, iter: I ) -> protocol::Alloc min_timestamp: Timestamp::max(), max_timestamp: Timestamp::min(), leaked_count: 0, - allocated_count: 0 + allocated_count: 0, } } } - let group = iter.fold_with( - Group::default(), - |mut group, allocation| { + let group = iter + .fold_with(Group::default(), |mut group, allocation| { let size = allocation.size; let timestamp = allocation.timestamp; group.size_sum += size; - group.min_size = min( group.min_size, size ); - group.max_size = max( group.max_size, size ); - group.min_timestamp = min( group.min_timestamp, timestamp ); - group.max_timestamp = max( group.max_timestamp, timestamp ); + group.min_size = min(group.min_size, size); + group.max_size = max(group.max_size, size); + group.min_timestamp = min(group.min_timestamp, timestamp); + group.max_timestamp = max(group.max_timestamp, timestamp); group.allocated_count += 1; if allocation.deallocation.is_none() { @@ -936,21 +993,21 @@ fn get_allocation_group_data< 'a, I >( data: &Data, iter: I ) -> protocol::Alloc } group - } - ).reduce( - || Group::default(), - |mut a, b| { - a.size_sum += b.size_sum; - a.min_size = min( a.min_size, b.min_size ); - a.max_size = max( a.max_size, b.max_size ); - a.min_timestamp = min( a.min_timestamp, b.min_timestamp ); - a.max_timestamp = max( a.max_timestamp, b.max_timestamp ); - a.allocated_count += b.allocated_count; - a.leaked_count += b.leaked_count; - - a - } - ); + }) + .reduce( + || Group::default(), + |mut a, b| { + a.size_sum += b.size_sum; + a.min_size = min(a.min_size, b.min_size); + a.max_size = max(a.max_size, b.max_size); + a.min_timestamp = min(a.min_timestamp, b.min_timestamp); + a.max_timestamp = max(a.max_timestamp, b.max_timestamp); + a.allocated_count += b.allocated_count; + a.leaked_count += b.leaked_count; + + a + }, + ); protocol::AllocationGroupData { leaked_count: group.leaked_count, @@ -960,10 +1017,10 @@ fn get_allocation_group_data< 'a, I >( data: &Data, iter: I ) -> protocol::Alloc max_size: group.max_size, min_timestamp: group.min_timestamp.into(), min_timestamp_relative: (group.min_timestamp - data.initial_timestamp()).into(), - min_timestamp_relative_p: timestamp_to_fraction( data, group.min_timestamp ), + min_timestamp_relative_p: timestamp_to_fraction(data, group.min_timestamp), max_timestamp: group.max_timestamp.into(), max_timestamp_relative: (group.max_timestamp - data.initial_timestamp()).into(), - max_timestamp_relative_p: timestamp_to_fraction( data, group.max_timestamp ), + max_timestamp_relative_p: timestamp_to_fraction(data, group.max_timestamp), interval: (group.max_timestamp - group.min_timestamp).into(), graph_url: None, graph_preview_url: None, @@ -973,8 +1030,8 @@ fn get_allocation_group_data< 'a, I >( data: &Data, iter: I ) -> protocol::Alloc } } -fn get_global_group_data( data: &Data, backtrace_id: BacktraceId ) -> protocol::AllocationGroupData { - let stats = data.get_group_statistics( backtrace_id ); +fn get_global_group_data(data: &Data, backtrace_id: BacktraceId) -> protocol::AllocationGroupData { + let stats = data.get_group_statistics(backtrace_id); let leaked_count = stats.alloc_count - stats.free_count; let allocated_count = stats.alloc_count; @@ -992,45 +1049,60 @@ fn get_global_group_data( data: &Data, backtrace_id: BacktraceId ) -> protocol:: max_size, min_timestamp: min_timestamp.into(), min_timestamp_relative: (min_timestamp - data.initial_timestamp()).into(), - min_timestamp_relative_p: timestamp_to_fraction( data, min_timestamp ), + min_timestamp_relative_p: timestamp_to_fraction(data, min_timestamp), max_timestamp: max_timestamp.into(), max_timestamp_relative: (max_timestamp - data.initial_timestamp()).into(), - max_timestamp_relative_p: timestamp_to_fraction( data, max_timestamp ), + max_timestamp_relative_p: timestamp_to_fraction(data, max_timestamp), interval: (max_timestamp - min_timestamp).into(), graph_url: None, graph_preview_url: None, - max_total_usage_first_seen_at: Some( stats.max_total_usage_first_seen_at.into() ), - max_total_usage_first_seen_at_relative: Some( (stats.max_total_usage_first_seen_at - data.initial_timestamp()).into() ), - max_total_usage_first_seen_at_relative_p: Some( timestamp_to_fraction( data, stats.max_total_usage_first_seen_at ) ), + max_total_usage_first_seen_at: Some(stats.max_total_usage_first_seen_at.into()), + max_total_usage_first_seen_at_relative: Some( + (stats.max_total_usage_first_seen_at - data.initial_timestamp()).into(), + ), + max_total_usage_first_seen_at_relative_p: Some(timestamp_to_fraction( + data, + stats.max_total_usage_first_seen_at, + )), } } -fn get_allocation_groups< 'a >( +fn get_allocation_groups<'a>( state: &'a State, - data: &'a Arc< Data >, + data: &'a Arc, backtrace_format: protocol::BacktraceFormat, params: protocol::RequestAllocationGroups, - allocation_groups: Arc< AllocationGroups > -) -> protocol::ResponseAllocationGroups< impl Serialize + 'a > { - let remaining = params.count.unwrap_or( -1_i32 as _ ) as usize; - let skip = params.skip.unwrap_or( 0 ) as usize; - let generate_graphs = params.generate_graphs.unwrap_or( false ); + allocation_groups: Arc, +) -> protocol::ResponseAllocationGroups { + let remaining = params.count.unwrap_or(-1_i32 as _) as usize; + let skip = params.skip.unwrap_or(0) as usize; + let generate_graphs = params.generate_graphs.unwrap_or(false); let total_count = allocation_groups.len(); let factory = move || { let backtrace_format = backtrace_format.clone(); let allocations = allocation_groups.clone(); (0..allocations.allocations_by_backtrace.len()) - .skip( skip ) - .take( remaining ) - .map( move |index| { - let (&backtrace_id, matched_allocation_ids) = allocations.allocations_by_backtrace.get( index ); - let all = get_global_group_data( data, backtrace_id ); - let mut only_matched = get_allocation_group_data( data, matched_allocation_ids.into_par_iter().map( |&allocation_id| data.get_allocation( allocation_id ) ) ); - let backtrace = data.get_backtrace( backtrace_id ).map( |(_, frame)| get_frame( data, &backtrace_format, frame ) ).collect(); + .skip(skip) + .take(remaining) + .map(move |index| { + let (&backtrace_id, matched_allocation_ids) = + allocations.allocations_by_backtrace.get(index); + let all = get_global_group_data(data, backtrace_id); + let mut only_matched = get_allocation_group_data( + data, + matched_allocation_ids + .into_par_iter() + .map(|&allocation_id| data.get_allocation(allocation_id)), + ); + let backtrace = data + .get_backtrace(backtrace_id) + .map(|(_, frame)| get_frame(data, &backtrace_format, frame)) + .collect(); if generate_graphs { - let code = format!( r#" + let code = format!( + r#" let graph = graph() .add("Matched", allocations()) .add("Global", data().allocations().only_matching_backtraces([{}])) @@ -1038,15 +1110,17 @@ fn get_allocation_groups< 'a >( .without_axes() .without_legend() .save(); - "#, backtrace_id.raw() ); + "#, + backtrace_id.raw() + ); let args = cli_core::script::EngineArgs { - data: Some( data.clone() ), - allocation_ids: Some( Arc::new( matched_allocation_ids.to_owned() ) ), - .. cli_core::script::EngineArgs::default() + data: Some(data.clone()), + allocation_ids: Some(Arc::new(matched_allocation_ids.to_owned())), + ..cli_core::script::EngineArgs::default() }; - let mut urls = state.generate_graphs( &data, args, &code ).into_iter(); + let mut urls = state.generate_graphs(&data, args, &code).into_iter(); only_matched.graph_url = urls.next(); only_matched.graph_preview_url = urls.next(); } @@ -1055,62 +1129,77 @@ fn get_allocation_groups< 'a >( all, only_matched, backtrace_id: backtrace_id.raw(), - backtrace + backtrace, } }) }; let response = protocol::ResponseAllocationGroups { - allocations: StreamingSerializer::new( factory ), - total_count: total_count as _ + allocations: StreamingSerializer::new(factory), + total_count: total_count as _, }; response } -fn handler_allocation_groups( req: HttpRequest ) -> Result< HttpResponse > { - let data = get_data( &req )?; - let filter_params: protocol::AllocFilter = query( &req )?; - let custom_filter: protocol::CustomFilter = query( &req )?; - let filter = prepare_allocation_filter( data, &filter_params, &custom_filter )?; - let backtrace_format: protocol::BacktraceFormat = query( &req )?; - let params: protocol::RequestAllocationGroups = query( &req )?; +async fn handler_allocation_groups(req: HttpRequest) -> Result { + let data = get_data(&req)?; + let filter_params: protocol::AllocFilter = query(&req)?; + let custom_filter: protocol::CustomFilter = query(&req)?; + let filter = prepare_allocation_filter(data, &filter_params, &custom_filter)?; + let backtrace_format: protocol::BacktraceFormat = query(&req)?; + let params: protocol::RequestAllocationGroups = query(&req)?; let key = AllocationGroupsKey { data_id: data.id(), filter: filter_params, custom_filter, - sort_by: params.sort_by.unwrap_or( protocol::AllocGroupsSortBy::MinTimestamp ), - order: params.order.unwrap_or( protocol::Order::Asc ) + sort_by: params + .sort_by + .unwrap_or(protocol::AllocGroupsSortBy::MinTimestamp), + order: params.order.unwrap_or(protocol::Order::Asc), }; - let groups = req.state().allocation_group_cache.lock().get( &key ).cloned(); - - fn sort_by< T, F >( data: &Data, groups: &mut AllocationGroups, order: protocol::Order, is_global: bool, callback: F ) - where F: Fn( &protocol::AllocationGroupData ) -> T + Send + Sync, - T: Ord + Send + Sync + let groups = req.state().allocation_group_cache.lock().get(&key).cloned(); + + fn sort_by( + data: &Data, + groups: &mut AllocationGroups, + order: protocol::Order, + is_global: bool, + callback: F, + ) where + F: Fn(&protocol::AllocationGroupData) -> T + Send + Sync, + T: Ord + Send + Sync, { if is_global { - groups.allocations_by_backtrace.par_sort_by_key( |(&backtrace_id, _)| { - let group_data = get_global_group_data( data, backtrace_id ); - callback( &group_data ) - }); + groups + .allocations_by_backtrace + .par_sort_by_key(|(&backtrace_id, _)| { + let group_data = get_global_group_data(data, backtrace_id); + callback(&group_data) + }); } else { - let key_for_backtrace: Vec< _ > = - groups.allocations_by_backtrace.par_iter().map( |(&backtrace_id, ids)| { - let allocations = ids.par_iter().map( |&id| data.get_allocation( id ) ); - let group_data = get_allocation_group_data( data, allocations ); - (backtrace_id, callback( &group_data )) - }).collect(); - - let key_for_backtrace: HashMap< _, _ > = key_for_backtrace.into_iter().collect(); - groups.allocations_by_backtrace.par_sort_by_key( |(&backtrace_id, _)| { - key_for_backtrace.get( &backtrace_id ).unwrap().clone() - }); + let key_for_backtrace: Vec<_> = groups + .allocations_by_backtrace + .par_iter() + .map(|(&backtrace_id, ids)| { + let allocations = ids.par_iter().map(|&id| data.get_allocation(id)); + let group_data = get_allocation_group_data(data, allocations); + (backtrace_id, callback(&group_data)) + }) + .collect(); + + let key_for_backtrace: HashMap<_, _> = key_for_backtrace.into_iter().collect(); + groups + .allocations_by_backtrace + .par_sort_by_key(|(&backtrace_id, _)| { + key_for_backtrace.get(&backtrace_id).unwrap().clone() + }); } match order { - protocol::Order::Asc => {}, + protocol::Order::Asc => {} protocol::Order::Dsc => { groups.allocations_by_backtrace.reverse(); } @@ -1118,196 +1207,256 @@ fn handler_allocation_groups( req: HttpRequest ) -> Result< HttpResponse > { } let allocation_groups; - if let Some( groups ) = groups { + if let Some(groups) = groups { allocation_groups = groups; } else { - let iter = prefiltered_allocation_ids( data, Default::default(), &filter ) + let iter = prefiltered_allocation_ids(data, Default::default(), &filter) .par_iter() - .map( |&allocation_id| (allocation_id, data.get_allocation( allocation_id )) ) - .filter( move |(id, allocation)| filter.try_match( data, *id, allocation ) ); + .map(|&allocation_id| (allocation_id, data.get_allocation(allocation_id))) + .filter(move |(id, allocation)| filter.try_match(data, *id, allocation)); - let mut groups = AllocationGroups::new( iter ); + let mut groups = AllocationGroups::new(iter); match key.sort_by { protocol::AllocGroupsSortBy::MinTimestamp => { - sort_by( data, &mut groups, key.order, false, |group_data| group_data.min_timestamp.clone() ); - }, + sort_by(data, &mut groups, key.order, false, |group_data| { + group_data.min_timestamp.clone() + }); + } protocol::AllocGroupsSortBy::MaxTimestamp => { - sort_by( data, &mut groups, key.order, false, |group_data| group_data.max_timestamp.clone() ); - }, + sort_by(data, &mut groups, key.order, false, |group_data| { + group_data.max_timestamp.clone() + }); + } protocol::AllocGroupsSortBy::Interval => { - sort_by( data, &mut groups, key.order, false, |group_data| group_data.interval.clone() ); - }, + sort_by(data, &mut groups, key.order, false, |group_data| { + group_data.interval.clone() + }); + } protocol::AllocGroupsSortBy::AllocatedCount => { - sort_by( data, &mut groups, key.order, false, |group_data| group_data.allocated_count ); - }, + sort_by(data, &mut groups, key.order, false, |group_data| { + group_data.allocated_count + }); + } protocol::AllocGroupsSortBy::LeakedCount => { - sort_by( data, &mut groups, key.order, false, |group_data| group_data.leaked_count ); - }, + sort_by(data, &mut groups, key.order, false, |group_data| { + group_data.leaked_count + }); + } protocol::AllocGroupsSortBy::Size => { - sort_by( data, &mut groups, key.order, false, |group_data| group_data.size ); - }, + sort_by(data, &mut groups, key.order, false, |group_data| { + group_data.size + }); + } protocol::AllocGroupsSortBy::GlobalMinTimestamp => { - sort_by( data, &mut groups, key.order, true, |group_data| group_data.min_timestamp.clone() ); - }, + sort_by(data, &mut groups, key.order, true, |group_data| { + group_data.min_timestamp.clone() + }); + } protocol::AllocGroupsSortBy::GlobalMaxTimestamp => { - sort_by( data, &mut groups, key.order, true, |group_data| group_data.max_timestamp.clone() ); - }, + sort_by(data, &mut groups, key.order, true, |group_data| { + group_data.max_timestamp.clone() + }); + } protocol::AllocGroupsSortBy::GlobalInterval => { - sort_by( data, &mut groups, key.order, true, |group_data| group_data.interval.clone() ); - }, + sort_by(data, &mut groups, key.order, true, |group_data| { + group_data.interval.clone() + }); + } protocol::AllocGroupsSortBy::GlobalAllocatedCount => { - sort_by( data, &mut groups, key.order, true, |group_data| group_data.allocated_count ); - }, + sort_by(data, &mut groups, key.order, true, |group_data| { + group_data.allocated_count + }); + } protocol::AllocGroupsSortBy::GlobalLeakedCount => { - sort_by( data, &mut groups, key.order, true, |group_data| group_data.leaked_count ); - }, + sort_by(data, &mut groups, key.order, true, |group_data| { + group_data.leaked_count + }); + } protocol::AllocGroupsSortBy::GlobalSize => { - sort_by( data, &mut groups, key.order, true, |group_data| group_data.size ); - }, + sort_by(data, &mut groups, key.order, true, |group_data| { + group_data.size + }); + } protocol::AllocGroupsSortBy::GlobalMaxTotalUsageFirstSeenAt => { - sort_by( data, &mut groups, key.order, true, |group_data| group_data.max_total_usage_first_seen_at.clone() ); + sort_by(data, &mut groups, key.order, true, |group_data| { + group_data.max_total_usage_first_seen_at.clone() + }); } } - allocation_groups = Arc::new( groups ); - req.state().allocation_group_cache.lock().put( key, allocation_groups.clone() ); + allocation_groups = Arc::new(groups); + req.state() + .allocation_group_cache + .lock() + .put(key, allocation_groups.clone()); } let state = req.state().clone(); - let body = async_data_handler( &req, move |data, tx| { - let response = get_allocation_groups( &state, &data, backtrace_format, params, allocation_groups ); - let _ = serde_json::to_writer( tx, &response ); + let body = async_data_handler(&req, move |data, tx| { + let response = + get_allocation_groups(&state, &data, backtrace_format, params, allocation_groups); + let _ = serde_json::to_writer(tx, &response); })?; - Ok( HttpResponse::Ok().content_type( "application/json" ).body( body ) ) + Ok(HttpResponse::Ok() + .content_type("application/json") + .body(body)) } -fn handler_raw_allocations( req: HttpRequest ) -> Result< HttpResponse > { - let data = get_data( &req )?; - let iter = data.alloc_sorted_by_timestamp( None, None ).iter().map( |&id| data.get_allocation( id ) ); +async fn handler_raw_allocations(req: HttpRequest) -> Result { + let data = get_data(&req)?; + let iter = data + .alloc_sorted_by_timestamp(None, None) + .iter() + .map(|&id| data.get_allocation(id)); let mut output = String::new(); - output.push_str( "[" ); + output.push_str("["); let mut is_first = true; for allocation in iter { if !is_first { - output.push_str( "," ); + output.push_str(","); } else { is_first = false; } - output.push_str( "{\"backtrace\":[" ); + output.push_str("{\"backtrace\":["); let mut is_first = true; - for (_, frame) in data.get_backtrace( allocation.backtrace ) { + for (_, frame) in data.get_backtrace(allocation.backtrace) { if !is_first { - output.push_str( "," ); + output.push_str(","); } else { is_first = false; } let address = frame.address().raw(); - write!( output, "\"{:016X}\"", address ).unwrap(); + write!(output, "\"{:016X}\"", address).unwrap(); } - output.push_str( "]}" ); + output.push_str("]}"); } - output.push_str( "]" ); - Ok( HttpResponse::Ok().content_type( "application/json" ).body( output ) ) + output.push_str("]"); + Ok(HttpResponse::Ok() + .content_type("application/json") + .body(output)) } -fn dump_node< T: fmt::Write, K: PartialEq + Clone, V, F: Fn( &mut T, &V ) -> fmt::Result >( - tree: &Tree< K, V >, +fn dump_node fmt::Result>( + tree: &Tree, node_id: NodeId, output: &mut T, - printer: &mut F + printer: &mut F, ) -> fmt::Result { - write!( output, "{{" )?; - - let node = tree.get_node( node_id ); - write!( output, "\"size\":{},", node.total_size )?; - write!( output, "\"count\":{},", node.total_count )?; - write!( output, "\"first\":{},", node.total_first_timestamp.as_secs() )?; - write!( output, "\"last\":{},", node.total_last_timestamp.as_secs() )?; - if let Some( value ) = node.value() { - write!( output, "\"value\":" )?; - printer( output, value )?; - write!( output, "," )?; + write!(output, "{{")?; + + let node = tree.get_node(node_id); + write!(output, "\"size\":{},", node.total_size)?; + write!(output, "\"count\":{},", node.total_count)?; + write!( + output, + "\"first\":{},", + node.total_first_timestamp.as_secs() + )?; + write!(output, "\"last\":{},", node.total_last_timestamp.as_secs())?; + if let Some(value) = node.value() { + write!(output, "\"value\":")?; + printer(output, value)?; + write!(output, ",")?; } - write!( output, "\"children\":[" )?; - for (index, &(_, child_id)) in tree.get_node( node_id ).children.iter().enumerate() { + write!(output, "\"children\":[")?; + for (index, &(_, child_id)) in tree.get_node(node_id).children.iter().enumerate() { if index != 0 { - write!( output, "," )?; + write!(output, ",")?; } - dump_node( tree, child_id, output, printer )?; + dump_node(tree, child_id, output, printer)?; } - write!( output, "]" )?; + write!(output, "]")?; - write!( output, "}}" )?; + write!(output, "}}")?; Ok(()) } -fn handler_tree( req: HttpRequest ) -> Result< HttpResponse > { - let data = get_data( &req )?; - let filter: protocol::AllocFilter = query( &req )?; - let custom_filter: protocol::CustomFilter = query( &req )?; - let filter = prepare_allocation_filter( data, &filter, &custom_filter )?; - let backtrace_format: protocol::BacktraceFormat = query( &req )?; +async fn handler_tree(req: HttpRequest) -> Result { + let data = get_data(&req)?; + let filter: protocol::AllocFilter = query(&req)?; + let custom_filter: protocol::CustomFilter = query(&req)?; + let filter = prepare_allocation_filter(data, &filter, &custom_filter)?; + let backtrace_format: protocol::BacktraceFormat = query(&req)?; - let body = async_data_handler( &req, move |data, mut tx| { - let mut tree: Tree< FrameId, &Frame > = Tree::new(); + let body = async_data_handler(&req, move |data, mut tx| { + let mut tree: Tree = Tree::new(); for (allocation_id, allocation) in data.allocations_with_id() { - if !filter.try_match( &data, allocation_id, allocation ) { + if !filter.try_match(&data, allocation_id, allocation) { continue; } - tree.add_allocation( allocation, allocation_id, data.get_backtrace( allocation.backtrace ) ); + tree.add_allocation( + allocation, + allocation_id, + data.get_backtrace(allocation.backtrace), + ); } - dump_node( &tree, 0, &mut tx, &mut |output, frame| { - let frame = get_frame( &data, &backtrace_format, frame ); - serde_json::to_writer( output, &frame ).map_err( |_| fmt::Error ) - }).unwrap(); + dump_node(&tree, 0, &mut tx, &mut |output, frame| { + let frame = get_frame(&data, &backtrace_format, frame); + serde_json::to_writer(output, &frame).map_err(|_| fmt::Error) + }) + .unwrap(); })?; - Ok( HttpResponse::Ok().content_type( "application/json" ).body( body ) ) + Ok(HttpResponse::Ok() + .content_type("application/json") + .body(body)) } -fn handler_backtrace( req: HttpRequest ) -> Result< HttpResponse > { - let data = get_data( &req )?; - let backtrace_id: u32 = req.match_info().get( "backtrace_id" ).unwrap().parse().unwrap(); - let backtrace_id = BacktraceId::new( backtrace_id ); - let backtrace = data.get_backtrace( backtrace_id ); - let backtrace_format: protocol::BacktraceFormat = query( &req )?; +async fn handler_backtrace(req: HttpRequest) -> Result { + let data = get_data(&req)?; + let backtrace_id: u32 = req + .match_info() + .get("backtrace_id") + .unwrap() + .parse() + .unwrap(); + let backtrace_id = BacktraceId::new(backtrace_id); + let backtrace = data.get_backtrace(backtrace_id); + let backtrace_format: protocol::BacktraceFormat = query(&req)?; let mut frames = Vec::new(); for (_, frame) in backtrace { - frames.push( get_frame( data, &backtrace_format, frame ) ); + frames.push(get_frame(data, &backtrace_format, frame)); } - let response = protocol::ResponseBacktrace { - frames - }; + let response = protocol::ResponseBacktrace { frames }; - Ok( HttpResponse::Ok().json( response ) ) + Ok(HttpResponse::Ok().json(response)) } -fn handler_backtraces( req: HttpRequest ) -> Result< HttpResponse > { - let backtrace_format: protocol::BacktraceFormat = query( &req )?; - let filter: protocol::BacktraceFilter = query( &req )?; - let filter = crate::filter::prepare_backtrace_filter( &filter )?; - let body = async_data_handler( &req, move |data, tx| { +async fn handler_backtraces(req: HttpRequest) -> Result { + let backtrace_format: protocol::BacktraceFormat = query(&req)?; + let filter: protocol::BacktraceFilter = query(&req)?; + let filter = crate::filter::prepare_backtrace_filter(&filter)?; + let body = async_data_handler(&req, move |data, tx| { let mut positive_cache = HashMap::new(); let mut negative_cache = HashMap::new(); - let total_count = data.all_backtraces().flat_map( |(_, backtrace)| { - if !crate::filter::match_backtrace( &data, &mut positive_cache, &mut negative_cache, &filter, backtrace ) { - None - } else { - Some(()) - } - }).count(); + let total_count = data + .all_backtraces() + .flat_map(|(_, backtrace)| { + if !crate::filter::match_backtrace( + &data, + &mut positive_cache, + &mut negative_cache, + &filter, + backtrace, + ) { + None + } else { + Some(()) + } + }) + .count(); let data = &data; let backtraces = move || { @@ -1315,224 +1464,269 @@ fn handler_backtraces( req: HttpRequest ) -> Result< HttpResponse > { let mut negative_cache = negative_cache.clone(); let backtrace_format = backtrace_format.clone(); let filter = filter.clone(); - data.all_backtraces().flat_map( move |(_, backtrace)| { - if !crate::filter::match_backtrace( &data, &mut positive_cache, &mut negative_cache, &filter, backtrace.clone() ) { + data.all_backtraces().flat_map(move |(_, backtrace)| { + if !crate::filter::match_backtrace( + &data, + &mut positive_cache, + &mut negative_cache, + &filter, + backtrace.clone(), + ) { return None; } let mut frames = Vec::new(); for (_, frame) in backtrace { - frames.push( get_frame( &data, &backtrace_format, frame ) ); + frames.push(get_frame(&data, &backtrace_format, frame)); } - Some( frames ) + Some(frames) }) }; let response = protocol::ResponseBacktraces { - backtraces: StreamingSerializer::new( backtraces ), - total_count: total_count as u64 + backtraces: StreamingSerializer::new(backtraces), + total_count: total_count as u64, }; - let _ = serde_json::to_writer( tx, &response ); + let _ = serde_json::to_writer(tx, &response); })?; - Ok( HttpResponse::Ok().content_type( "application/json" ).body( body ) ) + Ok(HttpResponse::Ok() + .content_type("application/json") + .body(body)) } -fn generate_regions< 'a, F: Fn( AllocationId, &Allocation ) -> bool + Clone + 'a >( data: &'a Data, filter: F ) -> impl Serialize + 'a { - let main_heap_start = data.alloc_sorted_by_address( None, None ) +fn generate_regions<'a, F: Fn(AllocationId, &Allocation) -> bool + Clone + 'a>( + data: &'a Data, + filter: F, +) -> impl Serialize + 'a { + let main_heap_start = data + .alloc_sorted_by_address(None, None) .iter() - .map( |&id| data.get_allocation( id ) ) - .filter( |allocation| !allocation.is_mmaped() && allocation.in_main_arena() ) - .map( |allocation| allocation.actual_range( data ).start ) + .map(|&id| data.get_allocation(id)) + .filter(|allocation| !allocation.is_mmaped() && allocation.in_main_arena()) + .map(|allocation| allocation.actual_range(data).start) .next() - .unwrap_or( 0 ); + .unwrap_or(0); - let main_heap_end = data.alloc_sorted_by_address( None, None ) + let main_heap_end = data + .alloc_sorted_by_address(None, None) .iter() - .map( |&id| data.get_allocation( id ) ) + .map(|&id| data.get_allocation(id)) .rev() - .filter( |allocation| !allocation.is_mmaped() && allocation.in_main_arena() ) - .map( |allocation| allocation.actual_range( data ).end ) + .filter(|allocation| !allocation.is_mmaped() && allocation.in_main_arena()) + .map(|allocation| allocation.actual_range(data).end) .next() - .unwrap_or( 0 ); + .unwrap_or(0); let regions = move || { let filter = filter.clone(); - data.alloc_sorted_by_address( None, None ) + data.alloc_sorted_by_address(None, None) .iter() - .map( move |&id| (id, data.get_allocation( id )) ) - .filter( move |(id, allocation)| filter( *id, allocation ) ) - .map( move |(_, allocation)| allocation.actual_range( data ) ) - .coalesce( |mut range, next_range| { + .map(move |&id| (id, data.get_allocation(id))) + .filter(move |(id, allocation)| filter(*id, allocation)) + .map(move |(_, allocation)| allocation.actual_range(data)) + .coalesce(|mut range, next_range| { if next_range.start <= range.end { range.end = next_range.end; - Ok( range ) + Ok(range) } else { - Err( (range, next_range) ) + Err((range, next_range)) } }) - .map( |range| [range.start, range.end] ) + .map(|range| [range.start, range.end]) }; protocol::ResponseRegions { main_heap_start, main_heap_end, - main_heap_start_s: format!( "{}", main_heap_start ), - main_heap_end_s: format!( "{}", main_heap_end ), - regions: StreamingSerializer::new( regions ) + main_heap_start_s: format!("{}", main_heap_start), + main_heap_end_s: format!("{}", main_heap_end), + regions: StreamingSerializer::new(regions), } } -fn handler_regions( req: HttpRequest ) -> Result< HttpResponse > { - let data = get_data( &req )?; - let filter: protocol::AllocFilter = query( &req )?; - let custom_filter: protocol::CustomFilter = query( &req )?; - let filter = prepare_allocation_filter( data, &filter, &custom_filter )?; +async fn handler_regions(req: HttpRequest) -> Result { + let data = get_data(&req)?; + let filter: protocol::AllocFilter = query(&req)?; + let custom_filter: protocol::CustomFilter = query(&req)?; + let filter = prepare_allocation_filter(data, &filter, &custom_filter)?; - let body = async_data_handler( &req, move |data, tx| { - let response = generate_regions( &data, |id, allocation| filter.try_match( &data, id, allocation ) ); - let _ = serde_json::to_writer( tx, &response ); + let body = async_data_handler(&req, move |data, tx| { + let response = generate_regions(&data, |id, allocation| { + filter.try_match(&data, id, allocation) + }); + let _ = serde_json::to_writer(tx, &response); })?; - Ok( HttpResponse::Ok().content_type( "application/json" ).body( body ) ) + Ok(HttpResponse::Ok() + .content_type("application/json") + .body(body)) } -fn handler_mallopts( req: HttpRequest ) -> Result< HttpResponse > { - let data = get_data( &req )?; - let backtrace_format: protocol::BacktraceFormat = query( &req )?; +async fn handler_mallopts(req: HttpRequest) -> Result { + let data = get_data(&req)?; + let backtrace_format: protocol::BacktraceFormat = query(&req)?; - let response: Vec< _ > = data.mallopts().iter().map( |mallopt| { - let mut backtrace = Vec::new(); - for (_, frame) in data.get_backtrace( mallopt.backtrace ) { - backtrace.push( get_frame( &data, &backtrace_format, frame ) ); - } + let response: Vec<_> = data + .mallopts() + .iter() + .map(|mallopt| { + let mut backtrace = Vec::new(); + for (_, frame) in data.get_backtrace(mallopt.backtrace) { + backtrace.push(get_frame(&data, &backtrace_format, frame)); + } - protocol::Mallopt { - timestamp: mallopt.timestamp.into(), - thread: mallopt.thread, - backtrace_id: mallopt.backtrace.raw(), - backtrace, - raw_param: mallopt.kind.raw(), - param: match mallopt.kind { - MalloptKind::TrimThreshold => Some( "M_TRIM_THRESHOLD" ), - MalloptKind::TopPad => Some( "M_TOP_PAD" ), - MalloptKind::MmapThreshold => Some( "M_MMAP_THRESHOLD" ), - MalloptKind::MmapMax => Some( "M_MMAP_MAX" ), - MalloptKind::CheckAction => Some( "M_CHECK_ACTION" ), - MalloptKind::Perturb => Some( "M_PERTURB" ), - MalloptKind::ArenaTest => Some( "M_ARENA_TEXT" ), - MalloptKind::ArenaMax => Some( "M_ARENA_MAX" ), - MalloptKind::Other( _ ) => None - }.map( |value| value.into() ), - value: mallopt.value, - result: mallopt.result - } - }).collect(); + protocol::Mallopt { + timestamp: mallopt.timestamp.into(), + thread: mallopt.thread, + backtrace_id: mallopt.backtrace.raw(), + backtrace, + raw_param: mallopt.kind.raw(), + param: match mallopt.kind { + MalloptKind::TrimThreshold => Some("M_TRIM_THRESHOLD"), + MalloptKind::TopPad => Some("M_TOP_PAD"), + MalloptKind::MmapThreshold => Some("M_MMAP_THRESHOLD"), + MalloptKind::MmapMax => Some("M_MMAP_MAX"), + MalloptKind::CheckAction => Some("M_CHECK_ACTION"), + MalloptKind::Perturb => Some("M_PERTURB"), + MalloptKind::ArenaTest => Some("M_ARENA_TEXT"), + MalloptKind::ArenaMax => Some("M_ARENA_MAX"), + MalloptKind::Other(_) => None, + } + .map(|value| value.into()), + value: mallopt.value, + result: mallopt.result, + } + }) + .collect(); - Ok( HttpResponse::Ok().json( response ) ) + Ok(HttpResponse::Ok().json(response)) } -fn handler_export_flamegraph_pl( req: HttpRequest ) -> Result< HttpResponse > { - let data = get_data( &req )?; - let filter: protocol::AllocFilter = query( &req )?; - let custom_filter: protocol::CustomFilter = query( &req )?; - let filter = prepare_allocation_filter( data, &filter, &custom_filter )?; +async fn handler_export_flamegraph_pl(req: HttpRequest) -> Result { + let data = get_data(&req)?; + let filter: protocol::AllocFilter = query(&req)?; + let custom_filter: protocol::CustomFilter = query(&req)?; + let filter = prepare_allocation_filter(data, &filter, &custom_filter)?; - let body = async_data_handler( &req, move |data, tx| { - let _ = export_as_flamegraph_pl( &data, tx, |id, allocation| filter.try_match( &data, id, allocation ) ); + let body = async_data_handler(&req, move |data, tx| { + let _ = export_as_flamegraph_pl(&data, tx, |id, allocation| { + filter.try_match(&data, id, allocation) + }); })?; - Ok( HttpResponse::Ok().content_type( "application/octet-stream" ).body( body ) ) + Ok(HttpResponse::Ok() + .content_type("application/octet-stream") + .body(body)) } -fn handler_export_flamegraph( req: HttpRequest ) -> Result< HttpResponse > { - let data = get_data( &req )?; - let filter: protocol::AllocFilter = query( &req )?; - let custom_filter: protocol::CustomFilter = query( &req )?; - let filter = prepare_allocation_filter( data, &filter, &custom_filter )?; +async fn handler_export_flamegraph(req: HttpRequest) -> Result { + let data = get_data(&req)?; + let filter: protocol::AllocFilter = query(&req)?; + let custom_filter: protocol::CustomFilter = query(&req)?; + let filter = prepare_allocation_filter(data, &filter, &custom_filter)?; - let body = async_data_handler( &req, move |data, tx| { - let _ = export_as_flamegraph( &data, tx, |id, allocation| filter.try_match( &data, id, allocation ) ); + let body = async_data_handler(&req, move |data, tx| { + let _ = export_as_flamegraph(&data, tx, |id, allocation| { + filter.try_match(&data, id, allocation) + }); })?; - Ok( HttpResponse::Ok().content_type( "image/svg+xml" ).body( body ) ) + Ok(HttpResponse::Ok().content_type("image/svg+xml").body(body)) } -fn handler_export_replay( req: HttpRequest ) -> Result< HttpResponse > { - let data = get_data( &req )?; - let filter: protocol::AllocFilter = query( &req )?; - let custom_filter: protocol::CustomFilter = query( &req )?; - let filter = prepare_allocation_filter( data, &filter, &custom_filter )?; +async fn handler_export_replay(req: HttpRequest) -> Result { + let data = get_data(&req)?; + let filter: protocol::AllocFilter = query(&req)?; + let custom_filter: protocol::CustomFilter = query(&req)?; + let filter = prepare_allocation_filter(data, &filter, &custom_filter)?; - let body = async_data_handler( &req, move |data, tx| { - let _ = export_as_replay( &data, tx, |id, allocation| filter.try_match( &data, id, allocation ) ); + let body = async_data_handler(&req, move |data, tx| { + let _ = export_as_replay(&data, tx, |id, allocation| { + filter.try_match(&data, id, allocation) + }); })?; - Ok( HttpResponse::Ok().content_type( "application/octet-stream" ).body( body ) ) + Ok(HttpResponse::Ok() + .content_type("application/octet-stream") + .body(body)) } -fn handler_export_heaptrack( req: HttpRequest ) -> Result< HttpResponse > { - let data = get_data( &req )?; - let filter: protocol::AllocFilter = query( &req )?; - let custom_filter: protocol::CustomFilter = query( &req )?; - let filter = prepare_allocation_filter( data, &filter, &custom_filter )?; +async fn handler_export_heaptrack(req: HttpRequest) -> Result { + let data = get_data(&req)?; + let filter: protocol::AllocFilter = query(&req)?; + let custom_filter: protocol::CustomFilter = query(&req)?; + let filter = prepare_allocation_filter(data, &filter, &custom_filter)?; - let body = async_data_handler( &req, move |data, tx| { - let _ = export_as_heaptrack( &data, tx, |id, allocation| filter.try_match( &data, id, allocation ) ); + let body = async_data_handler(&req, move |data, tx| { + let _ = export_as_heaptrack(&data, tx, |id, allocation| { + filter.try_match(&data, id, allocation) + }); })?; - Ok( HttpResponse::Ok().content_type( "application/octet-stream" ).body( body ) ) + Ok(HttpResponse::Ok() + .content_type("application/octet-stream") + .body(body)) } -fn handler_allocation_ascii_tree( req: HttpRequest ) -> Result< HttpResponse > { - let data = get_data( &req )?; - let filter: protocol::AllocFilter = query( &req )?; - let custom_filter: protocol::CustomFilter = query( &req )?; - let filter = prepare_allocation_filter( &data, &filter, &custom_filter )?; - - let body = async_data_handler( &req, move |data, mut tx| { - let tree = data.tree_by_source( |id, allocation| filter.try_match( &data, id, allocation ) ); - let table = data.dump_tree( &tree ); - let table = table_to_string( &table ); - let _ = writeln!( tx, "{}", table ); +async fn handler_allocation_ascii_tree(req: HttpRequest) -> Result { + let data = get_data(&req)?; + let filter: protocol::AllocFilter = query(&req)?; + let custom_filter: protocol::CustomFilter = query(&req)?; + let filter = prepare_allocation_filter(&data, &filter, &custom_filter)?; + + let body = async_data_handler(&req, move |data, mut tx| { + let tree = data.tree_by_source(|id, allocation| filter.try_match(&data, id, allocation)); + let table = data.dump_tree(&tree); + let table = table_to_string(&table); + let _ = writeln!(tx, "{}", table); })?; - Ok( HttpResponse::Ok().content_type( "text/plain; charset=utf-8" ).body( body ) ) + Ok(HttpResponse::Ok() + .content_type("text/plain; charset=utf-8") + .body(body)) } -fn handler_collation_json< F >( req: HttpRequest, callback: F ) -> Result< HttpResponse > - where F: Fn( &Data ) -> BTreeMap< String, BTreeMap< u32, CountAndSize > > + Send + 'static +async fn handler_collation_json(req: HttpRequest, callback: F) -> Result +where + F: Fn(&Data) -> BTreeMap> + Send + 'static, { use serde_json::json; - let body = async_data_handler( &req, move |data, tx| { - let constants = callback( &data ); + let body = async_data_handler(&req, move |data, tx| { + let constants = callback(&data); let mut total_count = 0; let mut total_size = 0; - let per_file: BTreeMap< _, _ > = constants.into_iter().map( |(key, per_line)| { - let mut whole_file_count = 0; - let mut whole_file_size = 0; - let per_line: BTreeMap< _, _ > = per_line.into_iter().map( |(line, entry)| { - whole_file_count += entry.count; - whole_file_size += entry.size; - total_count += entry.count; - total_size += entry.size; - let entry = json!({ - "count": entry.count, - "size": entry.size + let per_file: BTreeMap<_, _> = constants + .into_iter() + .map(|(key, per_line)| { + let mut whole_file_count = 0; + let mut whole_file_size = 0; + let per_line: BTreeMap<_, _> = per_line + .into_iter() + .map(|(line, entry)| { + whole_file_count += entry.count; + whole_file_size += entry.size; + total_count += entry.count; + total_size += entry.size; + let entry = json!({ + "count": entry.count, + "size": entry.size + }); + (line, entry) + }) + .collect(); + + let value = json!({ + "count": whole_file_count, + "size": whole_file_size, + "per_line": per_line }); - (line, entry) - }).collect(); - - let value = json!({ - "count": whole_file_count, - "size": whole_file_size, - "per_line": per_line - }); - (key, value) - }).collect(); + (key, value) + }) + .collect(); let response = json!({ "count": total_count, @@ -1540,151 +1734,163 @@ fn handler_collation_json< F >( req: HttpRequest, callback: F ) -> Result< HttpR "per_file": per_file }); - let _ = serde_json::to_writer( tx, &response ); + let _ = serde_json::to_writer(tx, &response); })?; - Ok( HttpResponse::Ok().content_type( "application/json; charset=utf-8" ).body( body ) ) + Ok(HttpResponse::Ok() + .content_type("application/json; charset=utf-8") + .body(body)) } -fn handler_dynamic_constants( req: HttpRequest ) -> Result< HttpResponse > { - handler_collation_json( req, |data| data.get_dynamic_constants() ) +async fn handler_dynamic_constants(req: HttpRequest) -> Result { + handler_collation_json(req, |data| data.get_dynamic_constants()).await } -fn handler_dynamic_statics( req: HttpRequest ) -> Result< HttpResponse > { - handler_collation_json( req, |data| data.get_dynamic_statics() ) +async fn handler_dynamic_statics(req: HttpRequest) -> Result { + handler_collation_json(req, |data| data.get_dynamic_statics()).await } -fn handler_dynamic_constants_ascii_tree( req: HttpRequest ) -> Result< HttpResponse > { - let body = async_data_handler( &req, move |data, mut tx| { +async fn handler_dynamic_constants_ascii_tree(req: HttpRequest) -> Result { + let body = async_data_handler(&req, move |data, mut tx| { let table = data.get_dynamic_constants_ascii_tree(); - let _ = writeln!( tx, "{}", table ); + let _ = writeln!(tx, "{}", table); })?; - Ok( HttpResponse::Ok().content_type( "text/plain; charset=utf-8" ).body( body ) ) + Ok(HttpResponse::Ok() + .content_type("text/plain; charset=utf-8") + .body(body)) } -fn handler_dynamic_statics_ascii_tree( req: HttpRequest ) -> Result< HttpResponse > { - let body = async_data_handler( &req, move |data, mut tx| { +async fn handler_dynamic_statics_ascii_tree(req: HttpRequest) -> Result { + let body = async_data_handler(&req, move |data, mut tx| { let table = data.get_dynamic_statics_ascii_tree(); - let _ = writeln!( tx, "{}", table ); + let _ = writeln!(tx, "{}", table); })?; - Ok( HttpResponse::Ok().content_type( "text/plain; charset=utf-8" ).body( body ) ) + Ok(HttpResponse::Ok() + .content_type("text/plain; charset=utf-8") + .body(body)) } -fn handler_script_files( req: HttpRequest ) -> Result< HttpResponse > { - let hash = req.match_info().get( "hash" ).unwrap(); - let entry = match req.state().generated_files.lock().by_hash.get( hash ) { - Some( entry ) => entry.clone(), +async fn handler_script_files(req: HttpRequest) -> Result { + let hash = req.match_info().get("hash").unwrap(); + let entry = match req.state().generated_files.lock().by_hash.get(hash) { + Some(entry) => entry.clone(), None => { - return Err( ErrorNotFound( "file not found" ) ); + return Err(ErrorNotFound("file not found")); } }; let (mut tx, rx) = byte_channel(); - let rx = rx.map_err( |_| ErrorInternalServerError( "internal error" ) ); - let rx = BodyStream::new( rx ); - let body = Body::Message( Box::new( rx ) ); + // let rx = rx.map_err(|_| ErrorInternalServerError("internal error")); + let rx = BodyStream::new(rx); + let body = BoxBody::new(rx); let mime = entry.mime; - thread::spawn( move || { + thread::spawn(move || { use std::io::Write; - tx.write_all( &entry.data ).unwrap(); + tx.write_all(&entry.data).unwrap(); }); - Ok( HttpResponse::Ok().content_type( mime ).body( body ) ) + Ok(HttpResponse::Ok().content_type(mime).body(body)) } -fn handler_allocation_filter_to_script( req: HttpRequest ) -> Result< HttpResponse > { - let data = get_data( &req )?; - let filter: protocol::AllocFilter = query( &req )?; - let filter = prepare_raw_allocation_filter( data, &filter )?; - let custom_filter: protocol::CustomFilter = query( &req )?; +async fn handler_allocation_filter_to_script(req: HttpRequest) -> Result { + let data = get_data(&req)?; + let filter: protocol::AllocFilter = query(&req)?; + let filter = prepare_raw_allocation_filter(data, &filter)?; + let custom_filter: protocol::CustomFilter = query(&req)?; let mut prologue = String::new(); let code; - if let Some( custom_filter ) = custom_filter.custom_filter { - prologue.push_str( "fn custom_filter() {\n" ); + if let Some(custom_filter) = custom_filter.custom_filter { + prologue.push_str("fn custom_filter() {\n"); for line in custom_filter.lines() { - prologue.push_str( " " ); - prologue.push_str( line ); - prologue.push_str( "\n" ); + prologue.push_str(" "); + prologue.push_str(line); + prologue.push_str("\n"); } - prologue.push_str( "}\n\n" ); - prologue.push_str( "let filtered = custom_filter();\n" ); - code = filter.to_code( Some( "filtered".into() ) ); + prologue.push_str("}\n\n"); + prologue.push_str("let filtered = custom_filter();\n"); + code = filter.to_code(Some("filtered".into())); } else { - code = filter.to_code( None ); + code = filter.to_code(None); } let body = serde_json::json! {{ "prologue": prologue, "code": code - }}; + }} + .to_string(); - Ok( HttpResponse::Ok().content_type( "application/json; charset=utf-8" ).body( body ) ) + Ok(HttpResponse::Ok() + .content_type("application/json; charset=utf-8") + .body(body)) } -fn handler_map_filter_to_script( req: HttpRequest ) -> Result< HttpResponse > { - let data = get_data( &req )?; - let filter: protocol::MapFilter = query( &req )?; - let filter = prepare_raw_map_filter( data, &filter )?; - let custom_filter: protocol::CustomFilter = query( &req )?; +async fn handler_map_filter_to_script(req: HttpRequest) -> Result { + let data = get_data(&req)?; + let filter: protocol::MapFilter = query(&req)?; + let filter = prepare_raw_map_filter(data, &filter)?; + let custom_filter: protocol::CustomFilter = query(&req)?; let mut prologue = String::new(); let code; - if let Some( custom_filter ) = custom_filter.custom_filter { - prologue.push_str( "fn custom_filter() {\n" ); + if let Some(custom_filter) = custom_filter.custom_filter { + prologue.push_str("fn custom_filter() {\n"); for line in custom_filter.lines() { - prologue.push_str( " " ); - prologue.push_str( line ); - prologue.push_str( "\n" ); + prologue.push_str(" "); + prologue.push_str(line); + prologue.push_str("\n"); } - prologue.push_str( "}\n\n" ); - prologue.push_str( "let filtered = custom_filter();\n" ); - code = filter.to_code( Some( "filtered".into() ) ); + prologue.push_str("}\n\n"); + prologue.push_str("let filtered = custom_filter();\n"); + code = filter.to_code(Some("filtered".into())); } else { - code = filter.to_code( None ); + code = filter.to_code(None); } let body = serde_json::json! {{ "prologue": prologue, "code": code - }}; + }} + .to_string(); - Ok( HttpResponse::Ok().content_type( "application/json; charset=utf-8" ).body( body ) ) + Ok(HttpResponse::Ok() + .content_type("application/json; charset=utf-8") + .body(body)) } -fn handler_execute_script( req: HttpRequest, body: web::Bytes ) -> Result< HttpResponse > { - let data = get_data( &req )?; - let body = String::from_utf8( body.to_vec() ).unwrap(); +async fn handler_execute_script(req: HttpRequest, body: web::Bytes) -> Result { + let data = get_data(&req)?; + let body = String::from_utf8(body.to_vec()).unwrap(); let args = cli_core::script::EngineArgs { - data: Some( data.clone() ), - .. cli_core::script::EngineArgs::default() + data: Some(data.clone()), + ..cli_core::script::EngineArgs::default() }; - let env = Arc::new( Mutex::new( cli_core::script::VirtualEnvironment::new() ) ); - let engine = cli_core::script::Engine::new( env.clone(), args ); + let env = Arc::new(Mutex::new(cli_core::script::VirtualEnvironment::new())); + let engine = cli_core::script::Engine::new(env.clone(), args); let timestamp = std::time::Instant::now(); - let result = engine.run( &body ); + let result = engine.run(&body); let elapsed = timestamp.elapsed(); let data_id = data.id(); let mut new_files = Vec::new(); let mut output = Vec::new(); - for item in std::mem::take( &mut env.lock().output ) { + for item in std::mem::take(&mut env.lock().output) { match item { - cli_core::script::ScriptOutputKind::PrintLine( line ) => { - output.push( serde_json::json! {{ + cli_core::script::ScriptOutputKind::PrintLine(line) => { + output.push(serde_json::json! {{ "kind": "println", "value": line }}); - }, + } cli_core::script::ScriptOutputKind::Image { path, data } => { - let hash = format!( "{:x}", md5::compute( &*data ) ); - let basename = path[ path.rfind( "/" ).unwrap() + 1.. ].to_owned(); - output.push( serde_json::json! {{ + let hash = format!("{:x}", md5::compute(&*data)); + let basename = path[path.rfind("/").unwrap() + 1..].to_owned(); + output.push(serde_json::json! {{ "url": format!( "/data/{}/script_files/{}/{}", data_id, hash, basename ), "kind": "image", "basename": basename, @@ -1696,10 +1902,10 @@ fn handler_execute_script( req: HttpRequest, body: web::Bytes ) -> Result< HttpR timestamp: Instant::now(), hash, mime: "image/svg+xml", - data + data, }; - new_files.push( entry ); + new_files.push(entry); } } } @@ -1707,19 +1913,19 @@ fn handler_execute_script( req: HttpRequest, body: web::Bytes ) -> Result< HttpR let mut generated = req.state().generated_files.lock(); generated.purge_old_if_too_big(); for entry in new_files { - generated.add_file( entry ); + generated.add_file(entry); } - std::mem::drop( generated ); + std::mem::drop(generated); let result = match result { - Ok( _ ) => { + Ok(_) => { serde_json::json! {{ "status": "ok", "elapsed": elapsed.as_secs_f64(), "output": output }} - }, - Err( error ) => { + } + Err(error) => { serde_json::json! {{ "status": "error", "message": error.message, @@ -1730,15 +1936,13 @@ fn handler_execute_script( req: HttpRequest, body: web::Bytes ) -> Result< HttpR } }; - Ok( - HttpResponse::Ok() - .content_type( "application/json; charset=utf-8" ) - .header( "Access-Control-Allow-Origin", "http://localhost:1234" ) - .body( serde_json::to_string( &result ).unwrap() ) - ) + Ok(HttpResponse::Ok() + .content_type("application/json; charset=utf-8") + .append_header(("Access-Control-Allow-Origin", "http://localhost:1234")) + .body(serde_json::to_string(&result).unwrap())) } -fn guess_mime( path: &str ) -> &str { +fn guess_mime(path: &str) -> &str { macro_rules! mimes { ($($ext:expr => $mime:expr),+) => { $( @@ -1761,131 +1965,242 @@ fn guess_mime( path: &str ) -> &str { "application/octet-stream" } -struct StaticResponse( &'static str, &'static [u8] ); -impl Responder for StaticResponse { - type Error = actix_web::Error; - type Future = Result< HttpResponse >; +#[derive(Clone)] +struct StaticResponse(&'static str, &'static [u8]); - fn respond_to( self, _: &HttpRequest ) -> Self::Future { - Ok( HttpResponse::Ok().content_type( guess_mime( self.0 ) ).body( self.1 ) ) +impl StaticResponse { + pub(crate) async fn respond(self) -> HttpResponse { + HttpResponse::Ok() + .content_type(guess_mime(self.0)) + .body(BoxBody::new(self.1)) } } - -include!( concat!( env!( "OUT_DIR" ), "/webui_assets.rs" ) ); +include!(concat!(env!("OUT_DIR"), "/webui_assets.rs")); #[derive(Debug)] pub enum ServerError { - BindFailed( io::Error ), - Other( io::Error ) + BindFailed(io::Error), + Other(io::Error), } impl fmt::Display for ServerError { - fn fmt( &self, fmt: &mut fmt::Formatter ) -> fmt::Result { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { match *self { - ServerError::BindFailed( ref error ) if error.kind() == io::ErrorKind::AddrInUse => { + ServerError::BindFailed(ref error) if error.kind() == io::ErrorKind::AddrInUse => { write!( fmt, "cannot server the server: address is already in use; you probably want to pick a different port with `--port`" ) - }, - ServerError::BindFailed( ref error ) => write!( fmt, "failed to start the server: {}", error ), - ServerError::Other( ref error ) => write!( fmt, "{}", error ) + } + ServerError::BindFailed(ref error) => { + write!(fmt, "failed to start the server: {}", error) + } + ServerError::Other(ref error) => write!(fmt, "{}", error), } } } -impl From< io::Error > for ServerError { - fn from( error: io::Error ) -> Self { - ServerError::Other( error ) +impl From for ServerError { + fn from(error: io::Error) -> Self { + ServerError::Other(error) } } impl Error for ServerError {} - -pub fn main( inputs: Vec< PathBuf >, debug_symbols: Vec< PathBuf >, load_in_parallel: bool, interface: &str, port: u16 ) -> Result< (), ServerError > { +pub async fn server_main( + inputs: Vec, + debug_symbols: Vec, + load_in_parallel: bool, + interface: &str, + port: u16, +) -> Result<(), ServerError> { let mut state = State::new(); if !load_in_parallel { for filename in inputs { - info!( "Trying to load {:?}...", filename ); - let fp = File::open( filename )?; - let data = Loader::load_from_stream( fp, &debug_symbols )?; - state.add_data( data ); + info!("Trying to load {:?}...", filename); + let fp = File::open(filename)?; + let data = Loader::load_from_stream(fp, &debug_symbols)?; + state.add_data(data); } } else { - let handles: Vec< thread::JoinHandle< io::Result< Data > > > = inputs.iter().map( move |filename| { - let filename = filename.clone(); - let debug_symbols = debug_symbols.clone(); - thread::spawn( move || { - info!( "Trying to load {:?}...", filename ); - let fp = File::open( filename )?; - let data = Loader::load_from_stream( fp, debug_symbols )?; - Ok( data ) + let handles: Vec>> = inputs + .iter() + .map(move |filename| { + let filename = filename.clone(); + let debug_symbols = debug_symbols.clone(); + thread::spawn(move || { + info!("Trying to load {:?}...", filename); + let fp = File::open(filename)?; + let data = Loader::load_from_stream(fp, debug_symbols)?; + Ok(data) + }) }) - }).collect(); - + .collect(); for handle in handles { let data = handle.join().unwrap()?; - state.add_data( data ); + state.add_data(data); } } for (key, bytes) in WEBUI_ASSETS { - debug!( "Static asset: '{}', length = {}", key, bytes.len() ); + debug!("Static asset: '{}', length = {}", key, bytes.len()); } - let state = Arc::new( state ); - let sys = actix::System::new( "server" ); - actix_web::HttpServer::new( move || { - App::new().data( state.clone() ) - .wrap( Cors::new() ) - .configure( |app| { - app - .service( web::resource( "/list" ).route( web::get().to( handler_list ) ) ) - .service( web::resource( "/data/{id}/timeline" ).route( web::get().to( handler_timeline ) ) ) - .service( web::resource( "/data/{id}/timeline_leaked" ).route( web::get().to( handler_timeline_leaked ) ) ) - .service( web::resource( "/data/{id}/timeline_maps" ).route( web::get().to( handler_timeline_maps ) ) ) - .service( web::resource( "/data/{id}/allocations" ).route( web::get().to( handler_allocations ) ) ) - .service( web::resource( "/data/{id}/allocation_groups" ).route( web::get().to( handler_allocation_groups ) ) ) - .service( web::resource( "/data/{id}/maps" ).route( web::get().to( handler_maps ) ) ) - .service( web::resource( "/data/{id}/backtraces" ).route( web::get().to( handler_backtraces ) ) ) - .service( web::resource( "/data/{id}/raw_allocations" ).route( web::get().to( handler_raw_allocations ) ) ) - .service( web::resource( "/data/{id}/tree" ).route( web::get().to( handler_tree ) ) ) - .service( web::resource( "/data/{id}/backtrace/{backtrace_id}" ).route( web::get().to( handler_backtrace ) ) ) - .service( web::resource( "/data/{id}/regions" ).route( web::get().to( handler_regions ) ) ) - .service( web::resource( "/data/{id}/mallopts" ).route( web::get().to( handler_mallopts ) ) ) - .service( web::resource( "/data/{id}/export/flamegraph" ).route( web::get().to( handler_export_flamegraph ) ) ) - .service( web::resource( "/data/{id}/export/flamegraph/{filename}" ).route( web::get().to( handler_export_flamegraph ) ) ) - .service( web::resource( "/data/{id}/export/flamegraph.pl" ).route( web::get().to( handler_export_flamegraph_pl ) ) ) - .service( web::resource( "/data/{id}/export/flamegraph.pl/{filename}" ).route( web::get().to( handler_export_flamegraph_pl ) ) ) - .service( web::resource( "/data/{id}/export/heaptrack" ).route( web::get().to( handler_export_heaptrack ) ) ) - .service( web::resource( "/data/{id}/export/heaptrack/{filename}" ).route( web::get().to( handler_export_heaptrack ) ) ) - .service( web::resource( "/data/{id}/export/replay" ).route( web::get().to( handler_export_replay ) ) ) - .service( web::resource( "/data/{id}/export/replay/{filename}" ).route( web::get().to( handler_export_replay ) ) ) - .service( web::resource( "/data/{id}/allocation_ascii_tree" ).route( web::get().to( handler_allocation_ascii_tree ) ) ) - .service( web::resource( "/data/{id}/dynamic_constants" ).route( web::get().to( handler_dynamic_constants ) ) ) - .service( web::resource( "/data/{id}/dynamic_constants/{filename}" ).route( web::get().to( handler_dynamic_constants ) ) ) - .service( web::resource( "/data/{id}/dynamic_constants_ascii_tree" ).route( web::get().to( handler_dynamic_constants_ascii_tree ) ) ) - .service( web::resource( "/data/{id}/dynamic_constants_ascii_tree/{filename}" ).route( web::get().to( handler_dynamic_constants_ascii_tree ) ) ) - .service( web::resource( "/data/{id}/dynamic_statics" ).route( web::get().to( handler_dynamic_statics ) ) ) - .service( web::resource( "/data/{id}/dynamic_statics/{filename}" ).route( web::get().to( handler_dynamic_statics ) ) ) - .service( web::resource( "/data/{id}/dynamic_statics_ascii_tree" ).route( web::get().to( handler_dynamic_statics_ascii_tree ) ) ) - .service( web::resource( "/data/{id}/dynamic_statics_ascii_tree/{filename}" ).route( web::get().to( handler_dynamic_statics_ascii_tree ) ) ) - .service( web::resource( "/data/{id}/execute_script" ).route( web::post().to( handler_execute_script ) ) ) - .service( web::resource( "/data/{id}/script_files/{hash}/{filename}" ).route( web::get().to( handler_script_files ) ) ) - .service( web::resource( "/data/{id}/allocation_filter_to_script" ).route( web::get().to( handler_allocation_filter_to_script ) ) ) - .service( web::resource( "/data/{id}/map_filter_to_script" ).route( web::get().to( handler_map_filter_to_script ) ) ) - ; + let state = Arc::new(state); + actix_web::HttpServer::new(move || { + App::new() + .app_data(web::Data::new(state.clone())) + .wrap(Cors::permissive()) + .configure(|app| { + app.service(web::resource("/list").route(web::get().to(handler_list))) + .service( + web::resource("/data/{id}/timeline").route(web::get().to(handler_timeline)), + ) + .service( + web::resource("/data/{id}/timeline_leaked") + .route(web::get().to(handler_timeline_leaked)), + ) + .service( + web::resource("/data/{id}/timeline_maps") + .route(web::get().to(handler_timeline_maps)), + ) + .service( + web::resource("/data/{id}/allocations") + .route(web::get().to(handler_allocations)), + ) + .service( + web::resource("/data/{id}/allocation_groups") + .route(web::get().to(handler_allocation_groups)), + ) + .service(web::resource("/data/{id}/maps").route(web::get().to(handler_maps))) + .service( + web::resource("/data/{id}/backtraces") + .route(web::get().to(handler_backtraces)), + ) + .service( + web::resource("/data/{id}/raw_allocations") + .route(web::get().to(handler_raw_allocations)), + ) + .service(web::resource("/data/{id}/tree").route(web::get().to(handler_tree))) + .service( + web::resource("/data/{id}/backtrace/{backtrace_id}") + .route(web::get().to(handler_backtrace)), + ) + .service( + web::resource("/data/{id}/regions").route(web::get().to(handler_regions)), + ) + .service( + web::resource("/data/{id}/mallopts").route(web::get().to(handler_mallopts)), + ) + .service( + web::resource("/data/{id}/export/flamegraph") + .route(web::get().to(handler_export_flamegraph)), + ) + .service( + web::resource("/data/{id}/export/flamegraph/{filename}") + .route(web::get().to(handler_export_flamegraph)), + ) + .service( + web::resource("/data/{id}/export/flamegraph.pl") + .route(web::get().to(handler_export_flamegraph_pl)), + ) + .service( + web::resource("/data/{id}/export/flamegraph.pl/{filename}") + .route(web::get().to(handler_export_flamegraph_pl)), + ) + .service( + web::resource("/data/{id}/export/heaptrack") + .route(web::get().to(handler_export_heaptrack)), + ) + .service( + web::resource("/data/{id}/export/heaptrack/{filename}") + .route(web::get().to(handler_export_heaptrack)), + ) + .service( + web::resource("/data/{id}/export/replay") + .route(web::get().to(handler_export_replay)), + ) + .service( + web::resource("/data/{id}/export/replay/{filename}") + .route(web::get().to(handler_export_replay)), + ) + .service( + web::resource("/data/{id}/allocation_ascii_tree") + .route(web::get().to(handler_allocation_ascii_tree)), + ) + .service( + web::resource("/data/{id}/dynamic_constants") + .route(web::get().to(handler_dynamic_constants)), + ) + .service( + web::resource("/data/{id}/dynamic_constants/{filename}") + .route(web::get().to(handler_dynamic_constants)), + ) + .service( + web::resource("/data/{id}/dynamic_constants_ascii_tree") + .route(web::get().to(handler_dynamic_constants_ascii_tree)), + ) + .service( + web::resource("/data/{id}/dynamic_constants_ascii_tree/{filename}") + .route(web::get().to(handler_dynamic_constants_ascii_tree)), + ) + .service( + web::resource("/data/{id}/dynamic_statics") + .route(web::get().to(handler_dynamic_statics)), + ) + .service( + web::resource("/data/{id}/dynamic_statics/{filename}") + .route(web::get().to(handler_dynamic_statics)), + ) + .service( + web::resource("/data/{id}/dynamic_statics_ascii_tree") + .route(web::get().to(handler_dynamic_statics_ascii_tree)), + ) + .service( + web::resource("/data/{id}/dynamic_statics_ascii_tree/{filename}") + .route(web::get().to(handler_dynamic_statics_ascii_tree)), + ) + .service( + web::resource("/data/{id}/execute_script") + .route(web::post().to(handler_execute_script)), + ) + .service( + web::resource("/data/{id}/script_files/{hash}/{filename}") + .route(web::get().to(handler_script_files)), + ) + .service( + web::resource("/data/{id}/allocation_filter_to_script") + .route(web::get().to(handler_allocation_filter_to_script)), + ) + .service( + web::resource("/data/{id}/map_filter_to_script") + .route(web::get().to(handler_map_filter_to_script)), + ); for (key, bytes) in WEBUI_ASSETS { - app.service( web::resource( &format!( "/{}", key ) ).route( web::get().to( move || StaticResponse( key, bytes ) ) ) ); + // app.service( + // web::resource(&format!("/{}", key)) + // .route(web::get().to::<_, HttpRequest>(StaticResponse(key, bytes))), + // ); + app.service(web::resource(&format!("/{}", key)).route(web::get().to( + move || { + let res = StaticResponse(key, bytes); + res.respond() + }, + ))); if *key == "index.html" { - app.service( web::resource( "/" ).route( web::get().to( move || StaticResponse( key, bytes ) ) ) ); + app.service(web::resource("/").route(web::get().to(move || { + let res = StaticResponse(key, bytes); + res.respond() + }))); } } }) - }).bind( &format!( "{}:{}", interface, port ) ).map_err( |err| ServerError::BindFailed( err ) )? - .shutdown_timeout( 1 ) - .start(); + }) + .bind(&format!("{}:{}", interface, port)) + .map_err(|err| ServerError::BindFailed(err))? + .shutdown_timeout(1) + .run() + .await?; - let _ = sys.run(); Ok(()) } diff --git a/server-core/src/protocol.rs b/server-core/src/protocol.rs index 33441edb..a44ad48e 100644 --- a/server-core/src/protocol.rs +++ b/server-core/src/protocol.rs @@ -1,63 +1,63 @@ use std::borrow::Cow; +use std::fmt; use std::marker::PhantomData; use std::str::FromStr; -use std::fmt; -use serde::Serialize; use cli_core::Timestamp; +use serde::Serialize; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize, Debug, Hash)] #[serde(transparent)] -pub struct Secs( u64 ); +pub struct Secs(u64); #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize, Debug, Hash)] #[serde(transparent)] -pub struct MSecs( u64 ); +pub struct MSecs(u64); #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize, Debug, Hash)] #[serde(transparent)] -pub struct FractNanos( u32 ); +pub struct FractNanos(u32); -impl From< Secs > for Timestamp { +impl From for Timestamp { #[inline] - fn from( value: Secs ) -> Self { - Timestamp::from_secs( value.0 ) + fn from(value: Secs) -> Self { + Timestamp::from_secs(value.0) } } -impl From< MSecs > for Timestamp { +impl From for Timestamp { #[inline] - fn from( value: MSecs ) -> Self { - Timestamp::from_msecs( value.0 ) + fn from(value: MSecs) -> Self { + Timestamp::from_msecs(value.0) } } -impl From< Timestamp > for Secs { +impl From for Secs { #[inline] - fn from( value: Timestamp ) -> Self { - Secs( value.as_secs() ) + fn from(value: Timestamp) -> Self { + Secs(value.as_secs()) } } -impl From< Timestamp > for FractNanos { +impl From for FractNanos { #[inline] - fn from( value: Timestamp ) -> Self { - FractNanos( value.fract_nsecs() as _ ) + fn from(value: Timestamp) -> Self { + FractNanos(value.fract_nsecs() as _) } } #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Serialize)] pub struct Timeval { pub secs: Secs, - pub fract_nsecs: FractNanos + pub fract_nsecs: FractNanos, } -impl From< Timestamp > for Timeval { +impl From for Timeval { #[inline] - fn from( value: Timestamp ) -> Self { + fn from(value: Timestamp) -> Self { Timeval { secs: value.into(), - fract_nsecs: value.into() + fract_nsecs: value.into(), } } } @@ -73,68 +73,68 @@ pub struct ResponseMetadata { pub runtime: Timeval, pub unique_backtrace_count: u64, pub maximum_backtrace_depth: u32, - pub timestamp: Timeval + pub timestamp: Timeval, } #[derive(Serialize)] pub struct ResponseTimeline { - pub xs: Vec< u64 >, - pub size_delta: Vec< i64 >, - pub count_delta: Vec< i64 >, - pub allocated_size: Vec< u64 >, - pub allocated_count: Vec< u64 >, - pub allocations: Vec< u32 >, - pub deallocations: Vec< u32 > + pub xs: Vec, + pub size_delta: Vec, + pub count_delta: Vec, + pub allocated_size: Vec, + pub allocated_count: Vec, + pub allocations: Vec, + pub deallocations: Vec, } #[derive(Serialize)] pub struct ResponseMapTimeline { - pub xs: Vec< u64 >, - pub address_space: Vec< i64 >, - pub rss: Vec< i64 >, - pub anonymous: Vec< i64 >, - pub dirty: Vec< i64 >, - pub clean: Vec< i64 >, - pub swap: Vec< i64 >, + pub xs: Vec, + pub address_space: Vec, + pub rss: Vec, + pub anonymous: Vec, + pub dirty: Vec, + pub clean: Vec, + pub swap: Vec, } #[derive(Clone, Serialize)] -pub struct Frame< 'a > { +pub struct Frame<'a> { pub address: u64, pub address_s: String, pub count: u64, #[serde(skip_serializing_if = "Option::is_none")] - pub library: Option< &'a str >, + pub library: Option<&'a str>, #[serde(skip_serializing_if = "Option::is_none")] - pub function: Option< Cow< 'a, str > >, + pub function: Option>, #[serde(skip_serializing_if = "Option::is_none")] - pub raw_function: Option< &'a str >, + pub raw_function: Option<&'a str>, #[serde(skip_serializing_if = "Option::is_none")] - pub source: Option< &'a str >, + pub source: Option<&'a str>, #[serde(skip_serializing_if = "Option::is_none")] - pub line: Option< u32 >, + pub line: Option, #[serde(skip_serializing_if = "Option::is_none")] - pub column: Option< u32 >, - pub is_inline: bool + pub column: Option, + pub is_inline: bool, } #[derive(Serialize)] -pub struct ResponseBacktrace< 'a > { - pub frames: Vec< Frame< 'a > > +pub struct ResponseBacktrace<'a> { + pub frames: Vec>, } #[derive(Serialize)] -pub struct Deallocation< 'a > { +pub struct Deallocation<'a> { pub timestamp: Timeval, pub thread: u32, #[serde(skip_serializing_if = "Option::is_none")] - pub backtrace_id: Option< u32 >, + pub backtrace_id: Option, #[serde(skip_serializing_if = "Option::is_none")] - pub backtrace: Option< Vec< Frame< 'a > > > + pub backtrace: Option>>, } #[derive(Serialize)] -pub struct Allocation< 'a > { +pub struct Allocation<'a> { pub id: u64, pub address: u64, pub address_s: String, @@ -145,16 +145,16 @@ pub struct Allocation< 'a > { pub size: u64, pub backtrace_id: u32, #[serde(skip_serializing_if = "Option::is_none")] - pub deallocation: Option< Deallocation< 'a > >, + pub deallocation: Option>, #[serde(skip_serializing_if = "Option::is_none")] - pub chain_deallocation: Option< Deallocation< 'a > >, - pub backtrace: Vec< Frame< 'a > >, + pub chain_deallocation: Option>, + pub backtrace: Vec>, pub is_mmaped: bool, pub is_jemalloc: bool, pub in_main_arena: bool, pub extra_space: u32, #[serde(skip_serializing_if = "Option::is_none")] - pub chain_lifetime: Option< Timeval >, + pub chain_lifetime: Option, pub position_in_chain: u32, pub chain_length: u32, } @@ -173,53 +173,53 @@ pub struct AllocationGroupData { pub interval: Timeval, pub leaked_count: u64, pub allocated_count: u64, - pub graph_preview_url: Option< String >, - pub graph_url: Option< String >, - pub max_total_usage_first_seen_at: Option< Timeval >, - pub max_total_usage_first_seen_at_relative: Option< Timeval >, - pub max_total_usage_first_seen_at_relative_p: Option< f32 >, + pub graph_preview_url: Option, + pub graph_url: Option, + pub max_total_usage_first_seen_at: Option, + pub max_total_usage_first_seen_at_relative: Option, + pub max_total_usage_first_seen_at_relative_p: Option, } #[derive(Serialize)] -pub struct AllocationGroup< 'a > { +pub struct AllocationGroup<'a> { pub all: AllocationGroupData, pub only_matched: AllocationGroupData, pub backtrace_id: u32, - pub backtrace: Vec< Frame< 'a > > + pub backtrace: Vec>, } #[derive(Clone, Serialize)] -pub struct MapSource< 'a > { +pub struct MapSource<'a> { pub timestamp: Timeval, pub timestamp_relative: Timeval, pub timestamp_relative_p: f32, pub thread: u32, pub backtrace_id: u32, - pub backtrace: Vec< Frame< 'a > > + pub backtrace: Vec>, } #[derive(Clone, Serialize)] -pub struct MapDeallocation< 'a > { +pub struct MapDeallocation<'a> { pub timestamp: Timeval, #[serde(skip_serializing_if = "Option::is_none")] - pub source: Option< MapSource< 'a > > + pub source: Option>, } #[derive(Clone, Serialize)] -pub struct MapRegionDeallocationSource< 'a > { +pub struct MapRegionDeallocationSource<'a> { pub address: u64, pub length: u64, - pub source: MapSource< 'a > + pub source: MapSource<'a>, } #[derive(Clone, Serialize)] -pub struct MapRegionDeallocation< 'a > { +pub struct MapRegionDeallocation<'a> { pub timestamp: Timeval, - pub sources: Vec< MapRegionDeallocationSource< 'a > > + pub sources: Vec>, } #[derive(Clone, Serialize)] -pub struct MapRegion< 'a > { +pub struct MapRegion<'a> { pub address: u64, pub address_s: String, pub timestamp: Timeval, @@ -227,7 +227,7 @@ pub struct MapRegion< 'a > { pub timestamp_relative_p: f32, pub size: u64, #[serde(skip_serializing_if = "Option::is_none")] - pub deallocation: Option< MapRegionDeallocation< 'a > >, + pub deallocation: Option>, pub is_readable: bool, pub is_writable: bool, pub is_executable: bool, @@ -236,11 +236,11 @@ pub struct MapRegion< 'a > { pub inode: u64, pub major: u32, pub minor: u32, - pub name: Cow< 'a, str >, + pub name: Cow<'a, str>, } #[derive(Serialize)] -pub struct Map< 'a > { +pub struct Map<'a> { pub id: u64, pub address: u64, pub address_s: String, @@ -249,21 +249,21 @@ pub struct Map< 'a > { pub timestamp_relative_p: f32, pub size: u64, #[serde(skip_serializing_if = "Option::is_none")] - pub source: Option< MapSource< 'a > >, + pub source: Option>, #[serde(skip_serializing_if = "Option::is_none")] - pub deallocation: Option< MapDeallocation< 'a > >, + pub deallocation: Option>, pub is_readable: bool, pub is_writable: bool, pub is_executable: bool, pub is_shared: bool, - pub name: Cow< 'a, str >, + pub name: Cow<'a, str>, pub peak_rss: u64, - pub graph_preview_url: Option< String >, - pub graph_url: Option< String >, + pub graph_preview_url: Option, + pub graph_url: Option, #[serde(skip_serializing_if = "Option::is_none")] - pub regions: Option< Vec< MapRegion< 'a > > >, + pub regions: Option>>, #[serde(skip_serializing_if = "Option::is_none")] - pub usage_history: Option< Vec< MapUsage > > + pub usage_history: Option>, } #[derive(Serialize)] @@ -282,54 +282,54 @@ pub struct MapUsage { } #[derive(Serialize)] -pub struct Mallopt< 'a > { +pub struct Mallopt<'a> { pub timestamp: Timeval, pub thread: u32, pub backtrace_id: u32, - pub backtrace: Vec< Frame< 'a > >, + pub backtrace: Vec>, pub raw_param: i32, #[serde(skip_serializing_if = "Option::is_none")] - pub param: Option< String >, + pub param: Option, pub value: i32, - pub result: i32 + pub result: i32, } #[derive(Serialize)] -pub struct ResponseAllocations< T: Serialize > { +pub struct ResponseAllocations { pub allocations: T, - pub total_count: u64 + pub total_count: u64, } #[derive(Serialize)] -pub struct ResponseAllocationGroups< T: Serialize > { +pub struct ResponseAllocationGroups { pub allocations: T, - pub total_count: u64 + pub total_count: u64, } #[derive(Serialize)] -pub struct ResponseMaps< T: Serialize > { +pub struct ResponseMaps { pub maps: T, - pub total_count: u64 + pub total_count: u64, } #[derive(Serialize)] -pub struct ResponseMmaps< T: Serialize > { - pub operations: T +pub struct ResponseMmaps { + pub operations: T, } #[derive(Serialize)] -pub struct ResponseRegions< T: Serialize > { +pub struct ResponseRegions { pub main_heap_start: u64, pub main_heap_end: u64, pub main_heap_start_s: String, pub main_heap_end_s: String, - pub regions: T + pub regions: T, } #[derive(Serialize)] -pub struct ResponseBacktraces< T: Serialize > { +pub struct ResponseBacktraces { pub backtraces: T, - pub total_count: u64 + pub total_count: u64, } #[derive(Copy, Clone, PartialEq, Eq, Deserialize, Debug, Hash)] @@ -343,7 +343,7 @@ pub enum LifetimeFilter { #[serde(rename = "only_temporary")] OnlyTemporary, #[serde(rename = "only_whole_group_leaked")] - OnlyWholeGroupLeaked + OnlyWholeGroupLeaked, } #[derive(Copy, Clone, PartialEq, Eq, Deserialize, Debug, Hash)] @@ -351,7 +351,7 @@ pub enum MmapedFilter { #[serde(rename = "yes")] Yes, #[serde(rename = "no")] - No + No, } #[derive(Copy, Clone, PartialEq, Eq, Deserialize, Debug, Hash)] @@ -359,7 +359,7 @@ pub enum JemallocFilter { #[serde(rename = "yes")] Yes, #[serde(rename = "no")] - No + No, } #[derive(Copy, Clone, PartialEq, Eq, Deserialize, Debug, Hash)] @@ -367,7 +367,7 @@ pub enum ArenaFilter { #[serde(rename = "main")] Main, #[serde(rename = "non_main")] - NonMain + NonMain, } #[derive(Copy, Clone, Deserialize, Debug)] @@ -377,7 +377,7 @@ pub enum AllocSortBy { #[serde(rename = "address")] Address, #[serde(rename = "size")] - Size + Size, } #[derive(Copy, Clone, PartialEq, Eq, Deserialize, Debug, Hash)] @@ -422,7 +422,7 @@ pub enum Order { #[serde(rename = "asc")] Asc, #[serde(rename = "dsc")] - Dsc + Dsc, } impl Default for Order { @@ -431,17 +431,17 @@ impl Default for Order { } } -fn get_while< 'a >( p: &mut &'a str, callback: impl Fn( char ) -> bool ) -> &'a str { +fn get_while<'a>(p: &mut &'a str, callback: impl Fn(char) -> bool) -> &'a str { let mut found = None; for (index, ch) in p.char_indices() { - if !callback( ch ) { - found = Some( index ); + if !callback(ch) { + found = Some(index); break; } } - if let Some( index ) = found { - let (before, after) = p.split_at( index ); + if let Some(index) = found { + let (before, after) = p.split_at(index); *p = after; before } else { @@ -455,79 +455,83 @@ fn get_while< 'a >( p: &mut &'a str, callback: impl Fn( char ) -> bool ) -> &'a pub struct IntervalParseError; #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] -pub struct Interval( pub Timestamp ); +pub struct Interval(pub Timestamp); impl FromStr for Interval { type Err = IntervalParseError; - fn from_str( string: &str ) -> Result< Self, Self::Err > { + fn from_str(string: &str) -> Result { let mut timestamp = Timestamp::min(); - let string = string.replace( " ", "" ); + let string = string.replace(" ", ""); let mut string = string.as_str(); while !string.is_empty() { - let number = get_while( &mut string, |ch| ch.is_digit( 10 ) || ch == ' ' ); - let unit = get_while( &mut string, |ch| ch.is_alphabetic() || ch == ' ' ); + let number = get_while(&mut string, |ch| ch.is_digit(10) || ch == ' '); + let unit = get_while(&mut string, |ch| ch.is_alphabetic() || ch == ' '); if number.is_empty() || (unit.is_empty() && !string.is_empty()) { - return Err( IntervalParseError ); + return Err(IntervalParseError); } let unit = match unit { - "h" | "H" => Timestamp::from_secs( 3600 ), - "m" | "M" => Timestamp::from_secs( 60 ), - "s" | "S" | "" => Timestamp::from_secs( 1 ), - "ms" | "MS" | "Ms" | "mS" => Timestamp::from_usecs( 1000 ), - "us" | "US" | "Us" | "uS" => Timestamp::from_usecs( 1 ), - _ => return Err( IntervalParseError ) + "h" | "H" => Timestamp::from_secs(3600), + "m" | "M" => Timestamp::from_secs(60), + "s" | "S" | "" => Timestamp::from_secs(1), + "ms" | "MS" | "Ms" | "mS" => Timestamp::from_usecs(1000), + "us" | "US" | "Us" | "uS" => Timestamp::from_usecs(1), + _ => return Err(IntervalParseError), }; - let number: u64 = number.parse().map_err( |_| IntervalParseError )?; + let number: u64 = number.parse().map_err(|_| IntervalParseError)?; let number = number as f64; timestamp = timestamp + (unit * number); } - Ok( Interval( timestamp ) ) + Ok(Interval(timestamp)) } } #[test] fn test_parse_interval() { - fn assert( string: &str, ts: Timestamp ) { + fn assert(string: &str, ts: Timestamp) { let x: Interval = string.parse().unwrap(); - assert_eq!( x.0, ts ); + assert_eq!(x.0, ts); } - assert( "1", Timestamp::from_secs( 1 ) ); - assert( "10", Timestamp::from_secs( 10 ) ); - assert( "10s", Timestamp::from_secs( 10 ) ); - assert( "3m", Timestamp::from_secs( 60 * 3 ) ); - assert( "3h", Timestamp::from_secs( 3600 * 3 ) ); - assert( "4h3m", Timestamp::from_secs( 3600 * 4 + 60 * 3 ) ); - assert( "4h3m2s", Timestamp::from_secs( 3600 * 4 + 60 * 3 + 2 ) ); - assert( "4h2s", Timestamp::from_secs( 3600 * 4 + 2 ) ); - assert( "1000ms", Timestamp::from_secs( 1 ) ); - assert( "100ms", Timestamp::from_usecs( 100_000 ) ); - assert( "100us", Timestamp::from_usecs( 100 ) ); -} - -impl< 'de > serde::Deserialize< 'de > for Interval { - fn deserialize< D >( deserializer: D ) -> Result< Self, D::Error > - where D: serde::Deserializer< 'de > + assert("1", Timestamp::from_secs(1)); + assert("10", Timestamp::from_secs(10)); + assert("10s", Timestamp::from_secs(10)); + assert("3m", Timestamp::from_secs(60 * 3)); + assert("3h", Timestamp::from_secs(3600 * 3)); + assert("4h3m", Timestamp::from_secs(3600 * 4 + 60 * 3)); + assert("4h3m2s", Timestamp::from_secs(3600 * 4 + 60 * 3 + 2)); + assert("4h2s", Timestamp::from_secs(3600 * 4 + 2)); + assert("1000ms", Timestamp::from_secs(1)); + assert("100ms", Timestamp::from_usecs(100_000)); + assert("100us", Timestamp::from_usecs(100)); +} + +impl<'de> serde::Deserialize<'de> for Interval { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, { struct Visitor; - impl< 'de > serde::de::Visitor< 'de > for Visitor { + impl<'de> serde::de::Visitor<'de> for Visitor { type Value = Interval; - fn expecting( &self, formatter: &mut fmt::Formatter ) -> fmt::Result { - write!( formatter, "interval" ) + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "interval") } - fn visit_str< E >( self, value: &str ) -> Result< Self::Value, E > - where E: serde::de::Error + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, { - let interval: Interval = value.parse().map_err( |_| E::custom( "not a valid interval" ) )?; - Ok( interval ) + let interval: Interval = value + .parse() + .map_err(|_| E::custom("not a valid interval"))?; + Ok(interval) } } - deserializer.deserialize_any( Visitor ) + deserializer.deserialize_any(Visitor) } } @@ -542,46 +546,61 @@ pub struct OffsetMin; pub struct OffsetMax; impl TimevalKind for Interval { - fn is_end_of_the_range() -> bool { false } - fn is_unitless_absolute() -> bool { false } + fn is_end_of_the_range() -> bool { + false + } + fn is_unitless_absolute() -> bool { + false + } } impl TimevalKind for OffsetMin { - fn is_end_of_the_range() -> bool { false } - fn is_unitless_absolute() -> bool { true } + fn is_end_of_the_range() -> bool { + false + } + fn is_unitless_absolute() -> bool { + true + } } impl TimevalKind for OffsetMax { - fn is_end_of_the_range() -> bool { true } - fn is_unitless_absolute() -> bool { true } + fn is_end_of_the_range() -> bool { + true + } + fn is_unitless_absolute() -> bool { + true + } } #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] -pub enum TimestampFilter< K: TimevalKind > { - Relative( Timestamp ), - Absolute( MSecs, PhantomData< K > ), - Percent( u32 ) +pub enum TimestampFilter { + Relative(Timestamp), + Absolute(MSecs, PhantomData), + Percent(u32), } -impl< K > TimestampFilter< K > where K: TimevalKind { - pub fn to_timestamp( &self, start_at: Timestamp, end_at: Timestamp ) -> Timestamp { +impl TimestampFilter +where + K: TimevalKind, +{ + pub fn to_timestamp(&self, start_at: Timestamp, end_at: Timestamp) -> Timestamp { match *self { - TimestampFilter::Absolute( msecs, _ ) => { + TimestampFilter::Absolute(msecs, _) => { let mut timestamp: Timestamp = msecs.into(); if K::is_end_of_the_range() { // We need to do this since the filter is specifed // in milliseconds while we use a higher precision timestamps // internally. if timestamp != Timestamp::max() { - timestamp = timestamp + Timestamp::from_msecs( 1 ) - Timestamp::eps(); + timestamp = timestamp + Timestamp::from_msecs(1) - Timestamp::eps(); } } if timestamp < start_at { - return Timestamp::from_secs( 0 ); + return Timestamp::from_secs(0); } timestamp - start_at - }, - TimestampFilter::Relative( timestamp ) => timestamp, - TimestampFilter::Percent( percentage ) => { + } + TimestampFilter::Relative(timestamp) => timestamp, + TimestampFilter::Percent(percentage) => { let range = end_at - start_at; let p = percentage as f64 / 100.0; let shift = range * p; @@ -592,156 +611,173 @@ impl< K > TimestampFilter< K > where K: TimevalKind { } } -impl< 'de, K > serde::Deserialize< 'de > for TimestampFilter< K > where K: TimevalKind { - fn deserialize< D >( deserializer: D ) -> Result< Self, D::Error > - where D: serde::Deserializer< 'de > +impl<'de, K> serde::Deserialize<'de> for TimestampFilter +where + K: TimevalKind, +{ + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, { - struct Visitor< K >( PhantomData< K > ); - impl< 'de, K > serde::de::Visitor< 'de > for Visitor< K > where K: TimevalKind { - type Value = TimestampFilter< K >; - - fn expecting( &self, formatter: &mut fmt::Formatter ) -> fmt::Result { - write!( formatter, "timestamp or percentage" ) + struct Visitor(PhantomData); + impl<'de, K> serde::de::Visitor<'de> for Visitor + where + K: TimevalKind, + { + type Value = TimestampFilter; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "timestamp or percentage") } - fn visit_str< E >( self, value: &str ) -> Result< Self::Value, E > - where E: serde::de::Error + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, { - if value.ends_with( "%" ) { - let value = value[ 0..value.len() - 1 ].parse().map_err( |_| E::custom( "not a valid percentage" ) )?; - Ok( TimestampFilter::Percent( value ) ) + if value.ends_with("%") { + let value = value[0..value.len() - 1] + .parse() + .map_err(|_| E::custom("not a valid percentage"))?; + Ok(TimestampFilter::Percent(value)) } else { if !K::is_unitless_absolute() { - let value: Interval = value.parse().map_err( |_| E::custom( "not a valid interval" ) )?; - Ok( TimestampFilter::Relative( value.0 ) ) + let value: Interval = value + .parse() + .map_err(|_| E::custom("not a valid interval"))?; + Ok(TimestampFilter::Relative(value.0)) } else { - let value: u64 = value.parse().map_err( |_| E::custom( "not a valid number" ) )?; - Ok( TimestampFilter::Absolute( MSecs( value ), PhantomData ) ) + let value: u64 = + value.parse().map_err(|_| E::custom("not a valid number"))?; + Ok(TimestampFilter::Absolute(MSecs(value), PhantomData)) } } } } - deserializer.deserialize_any( Visitor( PhantomData ) ) + deserializer.deserialize_any(Visitor(PhantomData)) } } #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] pub enum NumberOrPercentage { - Absolute( u32 ), - Percent( u32 ) + Absolute(u32), + Percent(u32), } impl NumberOrPercentage { - pub fn get( self, maximum: u32 ) -> u32 { + pub fn get(self, maximum: u32) -> u32 { match self { - NumberOrPercentage::Absolute( value ) => value, - NumberOrPercentage::Percent( percent ) => { + NumberOrPercentage::Absolute(value) => value, + NumberOrPercentage::Percent(percent) => { ((percent as f32 / 100.0) * maximum as f32) as _ } } } } -impl< 'de > serde::Deserialize< 'de > for NumberOrPercentage { - fn deserialize< D >( deserializer: D ) -> Result< Self, D::Error > - where D: serde::Deserializer< 'de > +impl<'de> serde::Deserialize<'de> for NumberOrPercentage { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, { struct Visitor; - impl< 'de > serde::de::Visitor< 'de > for Visitor { + impl<'de> serde::de::Visitor<'de> for Visitor { type Value = NumberOrPercentage; - fn expecting( &self, formatter: &mut fmt::Formatter ) -> fmt::Result { - write!( formatter, "number or percentage" ) + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "number or percentage") } - fn visit_str< E >( self, value: &str ) -> Result< Self::Value, E > - where E: serde::de::Error + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, { - if value.ends_with( "%" ) { - let value = value[ 0..value.len() - 1 ].parse().map_err( |_| E::custom( "not a valid percentage" ) )?; - Ok( NumberOrPercentage::Percent( value ) ) + if value.ends_with("%") { + let value = value[0..value.len() - 1] + .parse() + .map_err(|_| E::custom("not a valid percentage"))?; + Ok(NumberOrPercentage::Percent(value)) } else { - let value = value.parse().map_err( |_| E::custom( "not a valid number" ) )?; - Ok( NumberOrPercentage::Absolute( value ) ) + let value = value.parse().map_err(|_| E::custom("not a valid number"))?; + Ok(NumberOrPercentage::Absolute(value)) } } } - deserializer.deserialize_any( Visitor ) + deserializer.deserialize_any(Visitor) } } #[derive(Clone, PartialEq, Eq, Deserialize, Debug, Hash)] pub struct MmapFilter { - pub size_min: Option< u64 >, - pub size_max: Option< u64 >, + pub size_min: Option, + pub size_max: Option, } #[derive(Clone, PartialEq, Eq, Deserialize, Debug, Hash)] pub struct CustomFilter { - pub custom_filter: Option< String > + pub custom_filter: Option, } #[derive(Clone, PartialEq, Eq, Deserialize, Debug, Hash)] pub struct AllocFilter { - pub from: Option< TimestampFilter< OffsetMin > >, - pub to: Option< TimestampFilter< OffsetMax > >, - pub lifetime: Option< LifetimeFilter >, - pub address_min: Option< u64 >, - pub address_max: Option< u64 >, - pub size_min: Option< u64 >, - pub size_max: Option< u64 >, - pub first_size_min: Option< u64 >, - pub first_size_max: Option< u64 >, - pub last_size_min: Option< u64 >, - pub last_size_max: Option< u64 >, - pub lifetime_min: Option< Interval >, - pub lifetime_max: Option< Interval >, - pub backtrace_depth_min: Option< u32 >, - pub backtrace_depth_max: Option< u32 >, - pub backtraces: Option< u32 >, // TODO: Support multiple. - pub deallocation_backtraces: Option< u32 >, // TODO: Support multiple. - pub mmaped: Option< MmapedFilter >, - pub jemalloc: Option< JemallocFilter >, - pub arena: Option< ArenaFilter >, - pub function_regex: Option< String >, - pub source_regex: Option< String >, - pub negative_function_regex: Option< String >, - pub negative_source_regex: Option< String >, - pub marker: Option< u32 >, - pub group_interval_min: Option< TimestampFilter< Interval > >, - pub group_interval_max: Option< TimestampFilter< Interval > >, - pub group_max_total_usage_first_seen_min: Option< TimestampFilter< OffsetMin > >, - pub group_max_total_usage_first_seen_max: Option< TimestampFilter< OffsetMax > >, - pub group_leaked_allocations_min: Option< NumberOrPercentage >, - pub group_leaked_allocations_max: Option< NumberOrPercentage >, - pub group_allocations_min: Option< u32 >, - pub group_allocations_max: Option< u32 >, - pub chain_length_min: Option< u32 >, - pub chain_length_max: Option< u32 >, - pub chain_lifetime_min: Option< Interval >, - pub chain_lifetime_max: Option< Interval >, - pub position_in_chain_min: Option< u32 >, - pub position_in_chain_max: Option< u32 >, - pub alive_at: Option< TimestampFilter< OffsetMin > >, - pub alive_at_2: Option< TimestampFilter< OffsetMin > >, - pub from_map: Option< u64 >, + pub from: Option>, + pub to: Option>, + pub lifetime: Option, + pub address_min: Option, + pub address_max: Option, + pub size_min: Option, + pub size_max: Option, + pub first_size_min: Option, + pub first_size_max: Option, + pub last_size_min: Option, + pub last_size_max: Option, + pub lifetime_min: Option, + pub lifetime_max: Option, + pub backtrace_depth_min: Option, + pub backtrace_depth_max: Option, + pub backtraces: Option, // TODO: Support multiple. + pub deallocation_backtraces: Option, // TODO: Support multiple. + pub mmaped: Option, + pub jemalloc: Option, + pub arena: Option, + pub function_regex: Option, + pub source_regex: Option, + pub negative_function_regex: Option, + pub negative_source_regex: Option, + pub marker: Option, + pub group_interval_min: Option>, + pub group_interval_max: Option>, + pub group_max_total_usage_first_seen_min: Option>, + pub group_max_total_usage_first_seen_max: Option>, + pub group_leaked_allocations_min: Option, + pub group_leaked_allocations_max: Option, + pub group_allocations_min: Option, + pub group_allocations_max: Option, + pub chain_length_min: Option, + pub chain_length_max: Option, + pub chain_lifetime_min: Option, + pub chain_lifetime_max: Option, + pub position_in_chain_min: Option, + pub position_in_chain_max: Option, + pub alive_at: Option>, + pub alive_at_2: Option>, + pub from_map: Option, } #[derive(Clone, PartialEq, Eq, Deserialize, Debug, Hash)] pub struct BacktraceFilter { - pub backtrace_depth_min: Option< u32 >, - pub backtrace_depth_max: Option< u32 >, - pub function_regex: Option< String >, - pub source_regex: Option< String >, - pub negative_function_regex: Option< String >, - pub negative_source_regex: Option< String >, + pub backtrace_depth_min: Option, + pub backtrace_depth_max: Option, + pub function_regex: Option, + pub source_regex: Option, + pub negative_function_regex: Option, + pub negative_source_regex: Option, } #[derive(Clone, Deserialize, Debug)] pub struct BacktraceFormat { - pub strip_template_args: Option< bool > + pub strip_template_args: Option, } #[derive(Clone, PartialEq, Eq, Deserialize, Debug, Hash)] @@ -749,57 +785,57 @@ pub enum BoolFilter { #[serde(rename = "only_true")] OnlyTrue, #[serde(rename = "only_false")] - OnlyFalse + OnlyFalse, } #[derive(Clone, PartialEq, Eq, Deserialize, Debug, Hash)] pub struct MapFilter { - pub from: Option< TimestampFilter< OffsetMin > >, - pub to: Option< TimestampFilter< OffsetMax > >, - pub lifetime: Option< LifetimeFilter >, - pub address_min: Option< u64 >, - pub address_max: Option< u64 >, - pub size_min: Option< u64 >, - pub size_max: Option< u64 >, - pub lifetime_min: Option< Interval >, - pub lifetime_max: Option< Interval >, - pub backtrace_depth_min: Option< u32 >, - pub backtrace_depth_max: Option< u32 >, - pub backtraces: Option< u32 >, // TODO: Support multiple. - pub deallocation_backtraces: Option< u32 >, // TODO: Support multiple. - pub function_regex: Option< String >, - pub source_regex: Option< String >, - pub negative_function_regex: Option< String >, - pub negative_source_regex: Option< String >, - pub peak_rss_min: Option< u64 >, - pub peak_rss_max: Option< u64 >, - pub alive_at: Option< TimestampFilter< OffsetMin > >, - pub alive_at_2: Option< TimestampFilter< OffsetMin > >, - pub jemalloc: Option< BoolFilter >, - pub bytehound: Option< BoolFilter >, - pub readable: Option< BoolFilter >, - pub writable: Option< BoolFilter >, - pub executable: Option< BoolFilter >, + pub from: Option>, + pub to: Option>, + pub lifetime: Option, + pub address_min: Option, + pub address_max: Option, + pub size_min: Option, + pub size_max: Option, + pub lifetime_min: Option, + pub lifetime_max: Option, + pub backtrace_depth_min: Option, + pub backtrace_depth_max: Option, + pub backtraces: Option, // TODO: Support multiple. + pub deallocation_backtraces: Option, // TODO: Support multiple. + pub function_regex: Option, + pub source_regex: Option, + pub negative_function_regex: Option, + pub negative_source_regex: Option, + pub peak_rss_min: Option, + pub peak_rss_max: Option, + pub alive_at: Option>, + pub alive_at_2: Option>, + pub jemalloc: Option, + pub bytehound: Option, + pub readable: Option, + pub writable: Option, + pub executable: Option, } #[derive(Deserialize, Debug)] pub struct RequestAllocations { - pub skip: Option< u64 >, - pub count: Option< u32 >, + pub skip: Option, + pub count: Option, - pub sort_by: Option< AllocSortBy >, - pub order: Option< Order > + pub sort_by: Option, + pub order: Option, } #[derive(Deserialize, Debug)] pub struct RequestAllocationGroups { - pub skip: Option< u64 >, - pub count: Option< u32 >, + pub skip: Option, + pub count: Option, - pub sort_by: Option< AllocGroupsSortBy >, - pub order: Option< Order >, + pub sort_by: Option, + pub order: Option, - pub generate_graphs: Option< bool > + pub generate_graphs: Option, } #[derive(Copy, Clone, Deserialize, Debug)] @@ -816,15 +852,15 @@ pub enum MapsSortBy { #[derive(Deserialize, Debug)] pub struct RequestMaps { - pub skip: Option< u64 >, - pub count: Option< u32 >, + pub skip: Option, + pub count: Option, - pub sort_by: Option< MapsSortBy >, - pub order: Option< Order >, + pub sort_by: Option, + pub order: Option, - pub generate_graphs: Option< bool >, - pub with_regions: Option< bool >, - pub with_usage_history: Option< bool >, + pub generate_graphs: Option, + pub with_regions: Option, + pub with_usage_history: Option, - pub id: Option< u64 >, + pub id: Option, } diff --git a/server-core/src/streaming_channel.rs b/server-core/src/streaming_channel.rs index a37b6499..120b6a45 100644 --- a/server-core/src/streaming_channel.rs +++ b/server-core/src/streaming_channel.rs @@ -1,19 +1,23 @@ -use std::sync::{Arc, Mutex, Condvar}; use std::collections::VecDeque; +use std::io; +use std::pin::Pin; +use std::sync::{Arc, Condvar, Mutex}; +use std::task::{Context, Poll}; use futures; +use futures::task::AtomicWaker; -struct Inner< T > { - buffer: VecDeque< T >, - task: Option< futures::task::Task >, +struct Inner { + buffer: VecDeque, + waker: AtomicWaker, sender_closed: bool, - receiver_closed: bool + receiver_closed: bool, } -pub struct Sender< T >( Arc< (Condvar, Mutex< Inner< T > >) > ); +pub struct Sender(Arc<(Condvar, Mutex>)>); -impl< T > Sender< T > { - pub fn send( &mut self, value: T ) -> Result< (), () > { +impl Sender { + pub fn send(&mut self, value: T) -> Result<(), ()> { let mut inner = (self.0).1.lock().unwrap(); if inner.receiver_closed { inner.buffer.clear(); @@ -21,78 +25,71 @@ impl< T > Sender< T > { } while inner.buffer.len() >= 16 { - inner = (self.0).0.wait( inner ).unwrap(); + inner = (self.0).0.wait(inner).unwrap(); if inner.receiver_closed { inner.buffer.clear(); return Err(()); } } - inner.buffer.push_back( value ); + inner.buffer.push_back(value); - if let Some( ref mut task ) = inner.task { - task.notify(); - } + inner.waker.wake(); Ok(()) } } -impl< T > Drop for Sender< T > { - fn drop( &mut self ) { +impl Drop for Sender { + fn drop(&mut self) { let mut inner = (self.0).1.lock().unwrap(); inner.sender_closed = true; - if let Some( ref mut task ) = inner.task { - task.notify(); - } + inner.waker.wake(); } } -pub struct Receiver< T >( Arc< (Condvar, Mutex< Inner< T > >) > ); - -impl< T > futures::Stream for Receiver< T > { - type Item = T; - type Error = (); +pub struct Receiver(Arc<(Condvar, Mutex>)>); +impl futures_core::stream::Stream for Receiver { + type Item = Result; - fn poll( &mut self ) -> futures::Poll< Option< Self::Item >, Self::Error > { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let mut inner = (self.0).1.lock().unwrap(); match inner.buffer.pop_front() { - Some( value ) => { + Some(value) => { (self.0).0.notify_all(); - Ok( futures::Async::Ready( Some( value ) ) ) - }, + Poll::Ready(Some(Ok(value))) + } None => { if inner.sender_closed { - return Ok( futures::Async::Ready( None ) ); + return Poll::Ready(None); } - - inner.task = Some( futures::task::current() ); - Ok( futures::Async::NotReady ) + inner.waker.register(cx.waker()); + Poll::Pending } } } } -impl< T > Drop for Receiver< T > { - fn drop( &mut self ) { +impl Drop for Receiver { + fn drop(&mut self) { let mut inner = (self.0).1.lock().unwrap(); inner.receiver_closed = true; (self.0).0.notify_all(); } } -pub fn streaming_channel< T >() -> (Sender< T >, Receiver< T >) { +pub fn streaming_channel() -> (Sender, Receiver) { let inner = Inner { buffer: VecDeque::new(), - task: None, + waker: AtomicWaker::new(), sender_closed: false, - receiver_closed: false + receiver_closed: false, }; let condvar = Condvar::new(); - let inner = Arc::new( (condvar, Mutex::new( inner )) ); - let tx = Sender( inner.clone() ); - let rx = Receiver( inner.clone() ); + let inner = Arc::new((condvar, Mutex::new(inner))); + let tx = Sender(inner.clone()); + let rx = Receiver(inner.clone()); (tx, rx) } diff --git a/server-core/src/streaming_serializer.rs b/server-core/src/streaming_serializer.rs index b7c166df..fd33f609 100644 --- a/server-core/src/streaming_serializer.rs +++ b/server-core/src/streaming_serializer.rs @@ -1,35 +1,40 @@ use serde::{Serialize, Serializer}; use std::cell::RefCell; -pub struct StreamingSerializer< F, R, T > - where F: FnOnce() -> R, - R: Iterator< Item = T > +pub struct StreamingSerializer +where + F: FnOnce() -> R, + R: Iterator, { - callback: RefCell< Option< F > > + callback: RefCell>, } -impl< F, R, T > StreamingSerializer< F, R, T > - where F: FnOnce() -> R, - R: Iterator< Item = T >, - T: Serialize +impl StreamingSerializer +where + F: FnOnce() -> R, + R: Iterator, + T: Serialize, { - pub fn new( callback: F ) -> Self { - StreamingSerializer { callback: RefCell::new( Some( callback ) ) } + pub fn new(callback: F) -> Self { + StreamingSerializer { + callback: RefCell::new(Some(callback)), + } } } -impl< F, R, T > Serialize for StreamingSerializer< F, R, T > - where F: FnOnce() -> R, - R: Iterator< Item = T >, - T: Serialize +impl Serialize for StreamingSerializer +where + F: FnOnce() -> R, + R: Iterator, + T: Serialize, { - fn serialize< S: Serializer >( &self, serializer: S ) -> Result< S::Ok, S::Error > { + fn serialize(&self, serializer: S) -> Result { use serde::ser::SerializeSeq; let iter = (self.callback.borrow_mut().take().unwrap())(); - let mut seq = serializer.serialize_seq( None )?; + let mut seq = serializer.serialize_seq(None)?; for element in iter { - seq.serialize_element( &element )?; + seq.serialize_element(&element)?; } seq.end() diff --git a/webui/package.json b/webui/package.json index 0b2c0252..12432dfa 100644 --- a/webui/package.json +++ b/webui/package.json @@ -6,32 +6,35 @@ "license": "MIT", "private": true, "devDependencies": { - "@babel/core": "^7.12.0", - "@babel/preset-env": "^7.12.0", - "@babel/preset-react": "^7.12.0", - "parcel": "^2.0.0", + "@babel/core": "^7.21.3", + "@babel/preset-env": "^7.20.2", + "@babel/preset-react": "^7.18.6", + "parcel": "^2.8.3", "process": "^0.11.10" }, "dependencies": { - "bootstrap": "^4.1.1", - "bootswatch": "^4.1.1", - "classnames": "^2.3.1", - "dygraphs": "^2.1.0", - "feather-icons": "^4.7.3", + "bootstrap": "^5.2.3", + "bootswatch": "^5.2.3", + "classnames": "^2.3.2", + "dygraphs": "^2.2.1", + "feather-icons": "^4.29.0", "font-awesome": "^4.7.0", - "jquery": "^3.2.1", - "lodash": "^4.17.10", - "memoize-one": "^4.0.0", - "popper.js": "^1.14.3", - "prop-types": "^15.0.0", - "react": ">= 16.5", - "react-ace": "^9.4.1", - "react-contextmenu": "^2.11.0", - "react-dom": "^16.5.0", - "react-router-dom": "^5.2.0", - "react-table": "^6.8.6", + "jquery": "^3.6.4", + "lodash": "^4.17.21", + "memoize-one": "^6.0.0", + "popper.js": "^1.16.1", + "prop-types": "^15.8.1", + "react": "^18.2.0", + "react-ace": "^10.1.0", + "react-contextmenu": "^2.14.0", + "react-dom": "^18.2.0", + "react-router-dom": "^5.3.4", + "react-table": "^6.11.5", "reactdom": "^2.0.0", - "reactstrap": "^6.2.0" + "reactstrap": "^9.1.6" + }, + "optionalDependencies": { + "@parcel/css-linux-arm64-gnu": "^1.13.1" }, "scripts": { "start": "parcel ./src/index.html", diff --git a/webui/yarn.lock b/webui/yarn.lock index ee2a4407..f4d628f9 100644 --- a/webui/yarn.lock +++ b/webui/yarn.lock @@ -1,2883 +1,5224 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@ampproject/remapping@^2.1.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" - integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== - dependencies: - "@jridgewell/gen-mapping" "^0.1.0" - "@jridgewell/trace-mapping" "^0.3.9" - -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" - integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== - dependencies: - "@babel/highlight" "^7.16.7" - -"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.17.10": - version "7.18.5" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.18.5.tgz#acac0c839e317038c73137fbb6ef71a1d6238471" - integrity sha512-BxhE40PVCBxVEJsSBhB6UWyAuqJRxGsAw8BdHMJ3AKGydcwuWW4kOO3HmqBQAdcq/OP+/DlTVxLvsCzRTnZuGg== - -"@babel/core@^7.12.0": - version "7.18.5" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.5.tgz#c597fa680e58d571c28dda9827669c78cdd7f000" - integrity sha512-MGY8vg3DxMnctw0LdvSEojOsumc70g0t18gNyUdAZqB1Rpd1Bqo/svHGvt+UJ6JcGX+DIekGFDxxIWofBxLCnQ== - dependencies: - "@ampproject/remapping" "^2.1.0" - "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.18.2" - "@babel/helper-compilation-targets" "^7.18.2" - "@babel/helper-module-transforms" "^7.18.0" - "@babel/helpers" "^7.18.2" - "@babel/parser" "^7.18.5" - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.18.5" - "@babel/types" "^7.18.4" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.1" - semver "^6.3.0" - -"@babel/generator@^7.18.2": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.2.tgz#33873d6f89b21efe2da63fe554460f3df1c5880d" - integrity sha512-W1lG5vUwFvfMd8HVXqdfbuG7RuaSrTCCD8cl8fP8wOivdbtbIg2Db3IWUcgvfxKbbn6ZBGYRW/Zk1MIwK49mgw== - dependencies: - "@babel/types" "^7.18.2" - "@jridgewell/gen-mapping" "^0.3.0" - jsesc "^2.5.1" - -"@babel/helper-annotate-as-pure@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz#bb2339a7534a9c128e3102024c60760a3a7f3862" - integrity sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw== - dependencies: - "@babel/types" "^7.16.7" - -"@babel/helper-builder-binary-assignment-operator-visitor@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz#38d138561ea207f0f69eb1626a418e4f7e6a580b" - integrity sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA== - dependencies: - "@babel/helper-explode-assignable-expression" "^7.16.7" - "@babel/types" "^7.16.7" - -"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.16.7", "@babel/helper-compilation-targets@^7.17.10", "@babel/helper-compilation-targets@^7.18.2": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.2.tgz#67a85a10cbd5fc7f1457fec2e7f45441dc6c754b" - integrity sha512-s1jnPotJS9uQnzFtiZVBUxe67CuBa679oWFHpxYYnTpRL/1ffhyX44R9uYiXoa/pLXcY9H2moJta0iaanlk/rQ== - dependencies: - "@babel/compat-data" "^7.17.10" - "@babel/helper-validator-option" "^7.16.7" - browserslist "^4.20.2" - semver "^6.3.0" - -"@babel/helper-create-class-features-plugin@^7.17.12", "@babel/helper-create-class-features-plugin@^7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.0.tgz#fac430912606331cb075ea8d82f9a4c145a4da19" - integrity sha512-Kh8zTGR9de3J63e5nS0rQUdRs/kbtwoeQQ0sriS0lItjC96u8XXZN6lKpuyWd2coKSU13py/y+LTmThLuVX0Pg== - dependencies: - "@babel/helper-annotate-as-pure" "^7.16.7" - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-function-name" "^7.17.9" - "@babel/helper-member-expression-to-functions" "^7.17.7" - "@babel/helper-optimise-call-expression" "^7.16.7" - "@babel/helper-replace-supers" "^7.16.7" - "@babel/helper-split-export-declaration" "^7.16.7" - -"@babel/helper-create-regexp-features-plugin@^7.16.7", "@babel/helper-create-regexp-features-plugin@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.12.tgz#bb37ca467f9694bbe55b884ae7a5cc1e0084e4fd" - integrity sha512-b2aZrV4zvutr9AIa6/gA3wsZKRwTKYoDxYiFKcESS3Ug2GTXzwBEvMuuFLhCQpEnRXs1zng4ISAXSUxxKBIcxw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.16.7" - regexpu-core "^5.0.1" - -"@babel/helper-define-polyfill-provider@^0.3.1": - version "0.3.1" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz#52411b445bdb2e676869e5a74960d2d3826d2665" - integrity sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA== - dependencies: - "@babel/helper-compilation-targets" "^7.13.0" - "@babel/helper-module-imports" "^7.12.13" - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/traverse" "^7.13.0" - debug "^4.1.1" - lodash.debounce "^4.0.8" - resolve "^1.14.2" - semver "^6.1.2" - -"@babel/helper-environment-visitor@^7.16.7", "@babel/helper-environment-visitor@^7.18.2": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.2.tgz#8a6d2dedb53f6bf248e31b4baf38739ee4a637bd" - integrity sha512-14GQKWkX9oJzPiQQ7/J36FTXcD4kSp8egKjO9nINlSKiHITRA9q/R74qu8S9xlc/b/yjsJItQUeeh3xnGN0voQ== - -"@babel/helper-explode-assignable-expression@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz#12a6d8522fdd834f194e868af6354e8650242b7a" - integrity sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ== - dependencies: - "@babel/types" "^7.16.7" - -"@babel/helper-function-name@^7.16.7", "@babel/helper-function-name@^7.17.9": - version "7.17.9" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz#136fcd54bc1da82fcb47565cf16fd8e444b1ff12" - integrity sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg== - dependencies: - "@babel/template" "^7.16.7" - "@babel/types" "^7.17.0" - -"@babel/helper-hoist-variables@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz#86bcb19a77a509c7b77d0e22323ef588fa58c246" - integrity sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg== - dependencies: - "@babel/types" "^7.16.7" - -"@babel/helper-member-expression-to-functions@^7.17.7": - version "7.17.7" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz#a34013b57d8542a8c4ff8ba3f747c02452a4d8c4" - integrity sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw== - dependencies: - "@babel/types" "^7.17.0" - -"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" - integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg== - dependencies: - "@babel/types" "^7.16.7" - -"@babel/helper-module-transforms@^7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.0.tgz#baf05dec7a5875fb9235bd34ca18bad4e21221cd" - integrity sha512-kclUYSUBIjlvnzN2++K9f2qzYKFgjmnmjwL4zlmU5f8ZtzgWe8s0rUPSTGy2HmK4P8T52MQsS+HTQAgZd3dMEA== - dependencies: - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-module-imports" "^7.16.7" - "@babel/helper-simple-access" "^7.17.7" - "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/helper-validator-identifier" "^7.16.7" - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.18.0" - "@babel/types" "^7.18.0" - -"@babel/helper-optimise-call-expression@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz#a34e3560605abbd31a18546bd2aad3e6d9a174f2" - integrity sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w== - dependencies: - "@babel/types" "^7.16.7" - -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.17.12", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.17.12.tgz#86c2347da5acbf5583ba0a10aed4c9bf9da9cf96" - integrity sha512-JDkf04mqtN3y4iAbO1hv9U2ARpPyPL1zqyWs/2WG1pgSq9llHFjStX5jdxb84himgJm+8Ng+x0oiWF/nw/XQKA== - -"@babel/helper-remap-async-to-generator@^7.16.8": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz#29ffaade68a367e2ed09c90901986918d25e57e3" - integrity sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.16.7" - "@babel/helper-wrap-function" "^7.16.8" - "@babel/types" "^7.16.8" - -"@babel/helper-replace-supers@^7.16.7", "@babel/helper-replace-supers@^7.18.2": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.18.2.tgz#41fdfcc9abaf900e18ba6e5931816d9062a7b2e0" - integrity sha512-XzAIyxx+vFnrOxiQrToSUOzUOn0e1J2Li40ntddek1Y69AXUTXoDJ40/D5RdjFu7s7qHiaeoTiempZcbuVXh2Q== - dependencies: - "@babel/helper-environment-visitor" "^7.18.2" - "@babel/helper-member-expression-to-functions" "^7.17.7" - "@babel/helper-optimise-call-expression" "^7.16.7" - "@babel/traverse" "^7.18.2" - "@babel/types" "^7.18.2" - -"@babel/helper-simple-access@^7.17.7", "@babel/helper-simple-access@^7.18.2": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.18.2.tgz#4dc473c2169ac3a1c9f4a51cfcd091d1c36fcff9" - integrity sha512-7LIrjYzndorDY88MycupkpQLKS1AFfsVRm2k/9PtKScSy5tZq0McZTj+DiMRynboZfIqOKvo03pmhTaUgiD6fQ== - dependencies: - "@babel/types" "^7.18.2" - -"@babel/helper-skip-transparent-expression-wrappers@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz#0ee3388070147c3ae051e487eca3ebb0e2e8bb09" - integrity sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw== - dependencies: - "@babel/types" "^7.16.0" - -"@babel/helper-split-export-declaration@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz#0b648c0c42da9d3920d85ad585f2778620b8726b" - integrity sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw== - dependencies: - "@babel/types" "^7.16.7" - -"@babel/helper-validator-identifier@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" - integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== - -"@babel/helper-validator-option@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23" - integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ== - -"@babel/helper-wrap-function@^7.16.8": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz#58afda087c4cd235de92f7ceedebca2c41274200" - integrity sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw== - dependencies: - "@babel/helper-function-name" "^7.16.7" - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.16.8" - "@babel/types" "^7.16.8" - -"@babel/helpers@^7.18.2": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.18.2.tgz#970d74f0deadc3f5a938bfa250738eb4ac889384" - integrity sha512-j+d+u5xT5utcQSzrh9p+PaJX94h++KN+ng9b9WEJq7pkUPAd61FGqhjuUEdfknb3E/uDBb7ruwEeKkIxNJPIrg== - dependencies: - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.18.2" - "@babel/types" "^7.18.2" - -"@babel/highlight@^7.16.7": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.17.12.tgz#257de56ee5afbd20451ac0a75686b6b404257351" - integrity sha512-7yykMVF3hfZY2jsHZEEgLc+3x4o1O+fYyULu11GynEUQNwB6lua+IIQn1FiJxNucd5UlyJryrwsOh8PL9Sn8Qg== - dependencies: - "@babel/helper-validator-identifier" "^7.16.7" - chalk "^2.0.0" - js-tokens "^4.0.0" - -"@babel/parser@^7.16.7", "@babel/parser@^7.18.5": - version "7.18.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.5.tgz#337062363436a893a2d22faa60be5bb37091c83c" - integrity sha512-YZWVaglMiplo7v8f1oMQ5ZPQr0vn7HPeZXxXWsxXJRjGVrzUFn9OxFQl1sb5wzfootjA/yChhW84BV+383FSOw== - -"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.17.12.tgz#1dca338caaefca368639c9ffb095afbd4d420b1e" - integrity sha512-xCJQXl4EeQ3J9C4yOmpTrtVGmzpm2iSzyxbkZHw7UCnZBftHpF/hpII80uWVyVrc40ytIClHjgWGTG1g/yB+aw== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.17.12.tgz#0d498ec8f0374b1e2eb54b9cb2c4c78714c77753" - integrity sha512-/vt0hpIw0x4b6BLKUkwlvEoiGZYYLNZ96CzyHYPbtG2jZGz6LBe7/V+drYrc/d+ovrF9NBi0pmtvmNb/FsWtRQ== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" - "@babel/plugin-proposal-optional-chaining" "^7.17.12" - -"@babel/plugin-proposal-async-generator-functions@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.17.12.tgz#094a417e31ce7e692d84bab06c8e2a607cbeef03" - integrity sha512-RWVvqD1ooLKP6IqWTA5GyFVX2isGEgC5iFxKzfYOIy/QEFdxYyCybBDtIGjipHpb9bDWHzcqGqFakf+mVmBTdQ== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-remap-async-to-generator" "^7.16.8" - "@babel/plugin-syntax-async-generators" "^7.8.4" - -"@babel/plugin-proposal-class-properties@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.17.12.tgz#84f65c0cc247d46f40a6da99aadd6438315d80a4" - integrity sha512-U0mI9q8pW5Q9EaTHFPwSVusPMV/DV9Mm8p7csqROFLtIE9rBF5piLqyrBGigftALrBcsBGu4m38JneAe7ZDLXw== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.17.12" - "@babel/helper-plugin-utils" "^7.17.12" - -"@babel/plugin-proposal-class-static-block@^7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.0.tgz#7d02253156e3c3793bdb9f2faac3a1c05f0ba710" - integrity sha512-t+8LsRMMDE74c6sV7KShIw13sqbqd58tlqNrsWoWBTIMw7SVQ0cZ905wLNS/FBCy/3PyooRHLFFlfrUNyyz5lA== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.18.0" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/plugin-syntax-class-static-block" "^7.14.5" - -"@babel/plugin-proposal-dynamic-import@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz#c19c897eaa46b27634a00fee9fb7d829158704b2" - integrity sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg== - dependencies: - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - -"@babel/plugin-proposal-export-namespace-from@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.17.12.tgz#b22864ccd662db9606edb2287ea5fd1709f05378" - integrity sha512-j7Ye5EWdwoXOpRmo5QmRyHPsDIe6+u70ZYZrd7uz+ebPYFKfRcLcNu3Ro0vOlJ5zuv8rU7xa+GttNiRzX56snQ== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - -"@babel/plugin-proposal-json-strings@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.17.12.tgz#f4642951792437233216d8c1af370bb0fbff4664" - integrity sha512-rKJ+rKBoXwLnIn7n6o6fulViHMrOThz99ybH+hKHcOZbnN14VuMnH9fo2eHE69C8pO4uX1Q7t2HYYIDmv8VYkg== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/plugin-syntax-json-strings" "^7.8.3" - -"@babel/plugin-proposal-logical-assignment-operators@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.17.12.tgz#c64a1bcb2b0a6d0ed2ff674fd120f90ee4b88a23" - integrity sha512-EqFo2s1Z5yy+JeJu7SFfbIUtToJTVlC61/C7WLKDntSw4Sz6JNAIfL7zQ74VvirxpjB5kz/kIx0gCcb+5OEo2Q== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - -"@babel/plugin-proposal-nullish-coalescing-operator@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.17.12.tgz#1e93079bbc2cbc756f6db6a1925157c4a92b94be" - integrity sha512-ws/g3FSGVzv+VH86+QvgtuJL/kR67xaEIF2x0iPqdDfYW6ra6JF3lKVBkWynRLcNtIC1oCTfDRVxmm2mKzy+ag== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - -"@babel/plugin-proposal-numeric-separator@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz#d6b69f4af63fb38b6ca2558442a7fb191236eba9" - integrity sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw== - dependencies: - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - -"@babel/plugin-proposal-object-rest-spread@^7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.0.tgz#79f2390c892ba2a68ec112eb0d895cfbd11155e8" - integrity sha512-nbTv371eTrFabDfHLElkn9oyf9VG+VKK6WMzhY2o4eHKaG19BToD9947zzGMO6I/Irstx9d8CwX6njPNIAR/yw== - dependencies: - "@babel/compat-data" "^7.17.10" - "@babel/helper-compilation-targets" "^7.17.10" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.17.12" - -"@babel/plugin-proposal-optional-catch-binding@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz#c623a430674ffc4ab732fd0a0ae7722b67cb74cf" - integrity sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA== - dependencies: - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - -"@babel/plugin-proposal-optional-chaining@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.17.12.tgz#f96949e9bacace3a9066323a5cf90cfb9de67174" - integrity sha512-7wigcOs/Z4YWlK7xxjkvaIw84vGhDv/P1dFGQap0nHkc8gFKY/r+hXc8Qzf5k1gY7CvGIcHqAnOagVKJJ1wVOQ== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - -"@babel/plugin-proposal-private-methods@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.17.12.tgz#c2ca3a80beb7539289938da005ad525a038a819c" - integrity sha512-SllXoxo19HmxhDWm3luPz+cPhtoTSKLJE9PXshsfrOzBqs60QP0r8OaJItrPhAj0d7mZMnNF0Y1UUggCDgMz1A== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.17.12" - "@babel/helper-plugin-utils" "^7.17.12" - -"@babel/plugin-proposal-private-property-in-object@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.17.12.tgz#b02efb7f106d544667d91ae97405a9fd8c93952d" - integrity sha512-/6BtVi57CJfrtDNKfK5b66ydK2J5pXUKBKSPD2G1whamMuEnZWgoOIfO8Vf9F/DoD4izBLD/Au4NMQfruzzykg== - dependencies: - "@babel/helper-annotate-as-pure" "^7.16.7" - "@babel/helper-create-class-features-plugin" "^7.17.12" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/plugin-syntax-private-property-in-object" "^7.14.5" - -"@babel/plugin-proposal-unicode-property-regex@^7.17.12", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.17.12.tgz#3dbd7a67bd7f94c8238b394da112d86aaf32ad4d" - integrity sha512-Wb9qLjXf3ZazqXA7IvI7ozqRIXIGPtSo+L5coFmEkhTQK18ao4UDDD0zdTGAarmbLj2urpRwrc6893cu5Bfh0A== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.17.12" - "@babel/helper-plugin-utils" "^7.17.12" - -"@babel/plugin-syntax-async-generators@^7.8.4": - version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" - integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-class-properties@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" - integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-syntax-class-static-block@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" - integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-dynamic-import@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" - integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-export-namespace-from@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" - integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-syntax-import-assertions@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.17.12.tgz#58096a92b11b2e4e54b24c6a0cc0e5e607abcedd" - integrity sha512-n/loy2zkq9ZEM8tEOwON9wTQSTNDTDEz6NujPtJGLU7qObzT1N4c4YZZf8E6ATB2AjNQg/Ib2AIpO03EZaCehw== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - -"@babel/plugin-syntax-json-strings@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" - integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-jsx@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.17.12.tgz#834035b45061983a491f60096f61a2e7c5674a47" - integrity sha512-spyY3E3AURfxh/RHtjx5j6hs8am5NbUBGfcZ2vB3uShSpZdQyXSf5rR5Mk76vbtlAZOelyVQ71Fg0x9SG4fsog== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - -"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" - integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" - integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-numeric-separator@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" - integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-object-rest-spread@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" - integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-catch-binding@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" - integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-chaining@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" - integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-private-property-in-object@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" - integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-top-level-await@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" - integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-transform-arrow-functions@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.17.12.tgz#dddd783b473b1b1537ef46423e3944ff24898c45" - integrity sha512-PHln3CNi/49V+mza4xMwrg+WGYevSF1oaiXaC2EQfdp4HWlSjRsrDXWJiQBKpP7749u6vQ9mcry2uuFOv5CXvA== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - -"@babel/plugin-transform-async-to-generator@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.17.12.tgz#dbe5511e6b01eee1496c944e35cdfe3f58050832" - integrity sha512-J8dbrWIOO3orDzir57NRsjg4uxucvhby0L/KZuGsWDj0g7twWK3g7JhJhOrXtuXiw8MeiSdJ3E0OW9H8LYEzLQ== - dependencies: - "@babel/helper-module-imports" "^7.16.7" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-remap-async-to-generator" "^7.16.8" - -"@babel/plugin-transform-block-scoped-functions@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz#4d0d57d9632ef6062cdf354bb717102ee042a620" - integrity sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg== - dependencies: - "@babel/helper-plugin-utils" "^7.16.7" - -"@babel/plugin-transform-block-scoping@^7.17.12": - version "7.18.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.4.tgz#7988627b3e9186a13e4d7735dc9c34a056613fb9" - integrity sha512-+Hq10ye+jlvLEogSOtq4mKvtk7qwcUQ1f0Mrueai866C82f844Yom2cttfJdMdqRLTxWpsbfbkIkOIfovyUQXw== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - -"@babel/plugin-transform-classes@^7.17.12": - version "7.18.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.18.4.tgz#51310b812a090b846c784e47087fa6457baef814" - integrity sha512-e42NSG2mlKWgxKUAD9EJJSkZxR67+wZqzNxLSpc51T8tRU5SLFHsPmgYR5yr7sdgX4u+iHA1C5VafJ6AyImV3A== - dependencies: - "@babel/helper-annotate-as-pure" "^7.16.7" - "@babel/helper-environment-visitor" "^7.18.2" - "@babel/helper-function-name" "^7.17.9" - "@babel/helper-optimise-call-expression" "^7.16.7" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-replace-supers" "^7.18.2" - "@babel/helper-split-export-declaration" "^7.16.7" - globals "^11.1.0" - -"@babel/plugin-transform-computed-properties@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.17.12.tgz#bca616a83679698f3258e892ed422546e531387f" - integrity sha512-a7XINeplB5cQUWMg1E/GI1tFz3LfK021IjV1rj1ypE+R7jHm+pIHmHl25VNkZxtx9uuYp7ThGk8fur1HHG7PgQ== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - -"@babel/plugin-transform-destructuring@^7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.0.tgz#dc4f92587e291b4daa78aa20cc2d7a63aa11e858" - integrity sha512-Mo69klS79z6KEfrLg/1WkmVnB8javh75HX4pi2btjvlIoasuxilEyjtsQW6XPrubNd7AQy0MMaNIaQE4e7+PQw== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - -"@babel/plugin-transform-dotall-regex@^7.16.7", "@babel/plugin-transform-dotall-regex@^7.4.4": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz#6b2d67686fab15fb6a7fd4bd895d5982cfc81241" - integrity sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" - -"@babel/plugin-transform-duplicate-keys@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.17.12.tgz#a09aa709a3310013f8e48e0e23bc7ace0f21477c" - integrity sha512-EA5eYFUG6xeerdabina/xIoB95jJ17mAkR8ivx6ZSu9frKShBjpOGZPn511MTDTkiCO+zXnzNczvUM69YSf3Zw== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - -"@babel/plugin-transform-exponentiation-operator@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz#efa9862ef97e9e9e5f653f6ddc7b665e8536fe9b" - integrity sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA== - dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" - -"@babel/plugin-transform-for-of@^7.18.1": - version "7.18.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.1.tgz#ed14b657e162b72afbbb2b4cdad277bf2bb32036" - integrity sha512-+TTB5XwvJ5hZbO8xvl2H4XaMDOAK57zF4miuC9qQJgysPNEAZZ9Z69rdF5LJkozGdZrjBIUAIyKUWRMmebI7vg== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - -"@babel/plugin-transform-function-name@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz#5ab34375c64d61d083d7d2f05c38d90b97ec65cf" - integrity sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA== - dependencies: - "@babel/helper-compilation-targets" "^7.16.7" - "@babel/helper-function-name" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" - -"@babel/plugin-transform-literals@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.17.12.tgz#97131fbc6bbb261487105b4b3edbf9ebf9c830ae" - integrity sha512-8iRkvaTjJciWycPIZ9k9duu663FT7VrBdNqNgxnVXEFwOIp55JWcZd23VBRySYbnS3PwQ3rGiabJBBBGj5APmQ== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - -"@babel/plugin-transform-member-expression-literals@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz#6e5dcf906ef8a098e630149d14c867dd28f92384" - integrity sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw== - dependencies: - "@babel/helper-plugin-utils" "^7.16.7" - -"@babel/plugin-transform-modules-amd@^7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.0.tgz#7ef1002e67e36da3155edc8bf1ac9398064c02ed" - integrity sha512-h8FjOlYmdZwl7Xm2Ug4iX2j7Qy63NANI+NQVWQzv6r25fqgg7k2dZl03p95kvqNclglHs4FZ+isv4p1uXMA+QA== - dependencies: - "@babel/helper-module-transforms" "^7.18.0" - "@babel/helper-plugin-utils" "^7.17.12" - babel-plugin-dynamic-import-node "^2.3.3" - -"@babel/plugin-transform-modules-commonjs@^7.18.2": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.2.tgz#1aa8efa2e2a6e818b6a7f2235fceaf09bdb31e9e" - integrity sha512-f5A865gFPAJAEE0K7F/+nm5CmAE3y8AWlMBG9unu5j9+tk50UQVK0QS8RNxSp7MJf0wh97uYyLWt3Zvu71zyOQ== - dependencies: - "@babel/helper-module-transforms" "^7.18.0" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-simple-access" "^7.18.2" - babel-plugin-dynamic-import-node "^2.3.3" - -"@babel/plugin-transform-modules-systemjs@^7.18.0": - version "7.18.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.5.tgz#87f11c44fbfd3657be000d4897e192d9cb535996" - integrity sha512-SEewrhPpcqMF1V7DhnEbhVJLrC+nnYfe1E0piZMZXBpxi9WvZqWGwpsk7JYP7wPWeqaBh4gyKlBhHJu3uz5g4Q== - dependencies: - "@babel/helper-hoist-variables" "^7.16.7" - "@babel/helper-module-transforms" "^7.18.0" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-validator-identifier" "^7.16.7" - babel-plugin-dynamic-import-node "^2.3.3" - -"@babel/plugin-transform-modules-umd@^7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.0.tgz#56aac64a2c2a1922341129a4597d1fd5c3ff020f" - integrity sha512-d/zZ8I3BWli1tmROLxXLc9A6YXvGK8egMxHp+E/rRwMh1Kip0AP77VwZae3snEJ33iiWwvNv2+UIIhfalqhzZA== - dependencies: - "@babel/helper-module-transforms" "^7.18.0" - "@babel/helper-plugin-utils" "^7.17.12" - -"@babel/plugin-transform-named-capturing-groups-regex@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.17.12.tgz#9c4a5a5966e0434d515f2675c227fd8cc8606931" - integrity sha512-vWoWFM5CKaTeHrdUJ/3SIOTRV+MBVGybOC9mhJkaprGNt5demMymDW24yC74avb915/mIRe3TgNb/d8idvnCRA== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.17.12" - "@babel/helper-plugin-utils" "^7.17.12" - -"@babel/plugin-transform-new-target@^7.17.12": - version "7.18.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.5.tgz#8c228c4a07501dd12c95c5f23d1622131cc23931" - integrity sha512-TuRL5uGW4KXU6OsRj+mLp9BM7pO8e7SGNTEokQRRxHFkXYMFiy2jlKSZPFtI/mKORDzciH+hneskcSOp0gU8hg== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - -"@babel/plugin-transform-object-super@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz#ac359cf8d32cf4354d27a46867999490b6c32a94" - integrity sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw== - dependencies: - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/helper-replace-supers" "^7.16.7" - -"@babel/plugin-transform-parameters@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.17.12.tgz#eb467cd9586ff5ff115a9880d6fdbd4a846b7766" - integrity sha512-6qW4rWo1cyCdq1FkYri7AHpauchbGLXpdwnYsfxFb+KtddHENfsY5JZb35xUwkK5opOLcJ3BNd2l7PhRYGlwIA== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - -"@babel/plugin-transform-property-literals@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz#2dadac85155436f22c696c4827730e0fe1057a55" - integrity sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw== - dependencies: - "@babel/helper-plugin-utils" "^7.16.7" - -"@babel/plugin-transform-react-display-name@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.16.7.tgz#7b6d40d232f4c0f550ea348593db3b21e2404340" - integrity sha512-qgIg8BcZgd0G/Cz916D5+9kqX0c7nPZyXaP8R2tLNN5tkyIZdG5fEwBrxwplzSnjC1jvQmyMNVwUCZPcbGY7Pg== - dependencies: - "@babel/helper-plugin-utils" "^7.16.7" - -"@babel/plugin-transform-react-jsx-development@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.16.7.tgz#43a00724a3ed2557ed3f276a01a929e6686ac7b8" - integrity sha512-RMvQWvpla+xy6MlBpPlrKZCMRs2AGiHOGHY3xRwl0pEeim348dDyxeH4xBsMPbIMhujeq7ihE702eM2Ew0Wo+A== - dependencies: - "@babel/plugin-transform-react-jsx" "^7.16.7" - -"@babel/plugin-transform-react-jsx@^7.16.7", "@babel/plugin-transform-react-jsx@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.17.12.tgz#2aa20022709cd6a3f40b45d60603d5f269586dba" - integrity sha512-Lcaw8bxd1DKht3thfD4A12dqo1X16he1Lm8rIv8sTwjAYNInRS1qHa9aJoqvzpscItXvftKDCfaEQzwoVyXpEQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.16.7" - "@babel/helper-module-imports" "^7.16.7" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/plugin-syntax-jsx" "^7.17.12" - "@babel/types" "^7.17.12" - -"@babel/plugin-transform-react-pure-annotations@^7.16.7": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.0.tgz#ef82c8e310913f3522462c9ac967d395092f1954" - integrity sha512-6+0IK6ouvqDn9bmEG7mEyF/pwlJXVj5lwydybpyyH3D0A7Hftk+NCTdYjnLNZksn261xaOV5ksmp20pQEmc2RQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.16.7" - "@babel/helper-plugin-utils" "^7.17.12" - -"@babel/plugin-transform-regenerator@^7.18.0": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.0.tgz#44274d655eb3f1af3f3a574ba819d3f48caf99d5" - integrity sha512-C8YdRw9uzx25HSIzwA7EM7YP0FhCe5wNvJbZzjVNHHPGVcDJ3Aie+qGYYdS1oVQgn+B3eAIJbWFLrJ4Jipv7nw== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - regenerator-transform "^0.15.0" - -"@babel/plugin-transform-reserved-words@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.17.12.tgz#7dbd349f3cdffba751e817cf40ca1386732f652f" - integrity sha512-1KYqwbJV3Co03NIi14uEHW8P50Md6KqFgt0FfpHdK6oyAHQVTosgPuPSiWud1HX0oYJ1hGRRlk0fP87jFpqXZA== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - -"@babel/plugin-transform-shorthand-properties@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz#e8549ae4afcf8382f711794c0c7b6b934c5fbd2a" - integrity sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg== - dependencies: - "@babel/helper-plugin-utils" "^7.16.7" - -"@babel/plugin-transform-spread@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.17.12.tgz#c112cad3064299f03ea32afed1d659223935d1f5" - integrity sha512-9pgmuQAtFi3lpNUstvG9nGfk9DkrdmWNp9KeKPFmuZCpEnxRzYlS8JgwPjYj+1AWDOSvoGN0H30p1cBOmT/Svg== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" - -"@babel/plugin-transform-sticky-regex@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz#c84741d4f4a38072b9a1e2e3fd56d359552e8660" - integrity sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw== - dependencies: - "@babel/helper-plugin-utils" "^7.16.7" - -"@babel/plugin-transform-template-literals@^7.18.2": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.2.tgz#31ed6915721864847c48b656281d0098ea1add28" - integrity sha512-/cmuBVw9sZBGZVOMkpAEaVLwm4JmK2GZ1dFKOGGpMzEHWFmyZZ59lUU0PdRr8YNYeQdNzTDwuxP2X2gzydTc9g== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - -"@babel/plugin-transform-typeof-symbol@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.17.12.tgz#0f12f57ac35e98b35b4ed34829948d42bd0e6889" - integrity sha512-Q8y+Jp7ZdtSPXCThB6zjQ74N3lj0f6TDh1Hnf5B+sYlzQ8i5Pjp8gW0My79iekSpT4WnI06blqP6DT0OmaXXmw== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - -"@babel/plugin-transform-unicode-escapes@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz#da8717de7b3287a2c6d659750c964f302b31ece3" - integrity sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q== - dependencies: - "@babel/helper-plugin-utils" "^7.16.7" - -"@babel/plugin-transform-unicode-regex@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz#0f7aa4a501198976e25e82702574c34cfebe9ef2" - integrity sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" - -"@babel/preset-env@^7.12.0": - version "7.18.2" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.18.2.tgz#f47d3000a098617926e674c945d95a28cb90977a" - integrity sha512-PfpdxotV6afmXMU47S08F9ZKIm2bJIQ0YbAAtDfIENX7G1NUAXigLREh69CWDjtgUy7dYn7bsMzkgdtAlmS68Q== - dependencies: - "@babel/compat-data" "^7.17.10" - "@babel/helper-compilation-targets" "^7.18.2" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-validator-option" "^7.16.7" - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.17.12" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.17.12" - "@babel/plugin-proposal-async-generator-functions" "^7.17.12" - "@babel/plugin-proposal-class-properties" "^7.17.12" - "@babel/plugin-proposal-class-static-block" "^7.18.0" - "@babel/plugin-proposal-dynamic-import" "^7.16.7" - "@babel/plugin-proposal-export-namespace-from" "^7.17.12" - "@babel/plugin-proposal-json-strings" "^7.17.12" - "@babel/plugin-proposal-logical-assignment-operators" "^7.17.12" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.17.12" - "@babel/plugin-proposal-numeric-separator" "^7.16.7" - "@babel/plugin-proposal-object-rest-spread" "^7.18.0" - "@babel/plugin-proposal-optional-catch-binding" "^7.16.7" - "@babel/plugin-proposal-optional-chaining" "^7.17.12" - "@babel/plugin-proposal-private-methods" "^7.17.12" - "@babel/plugin-proposal-private-property-in-object" "^7.17.12" - "@babel/plugin-proposal-unicode-property-regex" "^7.17.12" - "@babel/plugin-syntax-async-generators" "^7.8.4" - "@babel/plugin-syntax-class-properties" "^7.12.13" - "@babel/plugin-syntax-class-static-block" "^7.14.5" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - "@babel/plugin-syntax-import-assertions" "^7.17.12" - "@babel/plugin-syntax-json-strings" "^7.8.3" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - "@babel/plugin-syntax-private-property-in-object" "^7.14.5" - "@babel/plugin-syntax-top-level-await" "^7.14.5" - "@babel/plugin-transform-arrow-functions" "^7.17.12" - "@babel/plugin-transform-async-to-generator" "^7.17.12" - "@babel/plugin-transform-block-scoped-functions" "^7.16.7" - "@babel/plugin-transform-block-scoping" "^7.17.12" - "@babel/plugin-transform-classes" "^7.17.12" - "@babel/plugin-transform-computed-properties" "^7.17.12" - "@babel/plugin-transform-destructuring" "^7.18.0" - "@babel/plugin-transform-dotall-regex" "^7.16.7" - "@babel/plugin-transform-duplicate-keys" "^7.17.12" - "@babel/plugin-transform-exponentiation-operator" "^7.16.7" - "@babel/plugin-transform-for-of" "^7.18.1" - "@babel/plugin-transform-function-name" "^7.16.7" - "@babel/plugin-transform-literals" "^7.17.12" - "@babel/plugin-transform-member-expression-literals" "^7.16.7" - "@babel/plugin-transform-modules-amd" "^7.18.0" - "@babel/plugin-transform-modules-commonjs" "^7.18.2" - "@babel/plugin-transform-modules-systemjs" "^7.18.0" - "@babel/plugin-transform-modules-umd" "^7.18.0" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.17.12" - "@babel/plugin-transform-new-target" "^7.17.12" - "@babel/plugin-transform-object-super" "^7.16.7" - "@babel/plugin-transform-parameters" "^7.17.12" - "@babel/plugin-transform-property-literals" "^7.16.7" - "@babel/plugin-transform-regenerator" "^7.18.0" - "@babel/plugin-transform-reserved-words" "^7.17.12" - "@babel/plugin-transform-shorthand-properties" "^7.16.7" - "@babel/plugin-transform-spread" "^7.17.12" - "@babel/plugin-transform-sticky-regex" "^7.16.7" - "@babel/plugin-transform-template-literals" "^7.18.2" - "@babel/plugin-transform-typeof-symbol" "^7.17.12" - "@babel/plugin-transform-unicode-escapes" "^7.16.7" - "@babel/plugin-transform-unicode-regex" "^7.16.7" - "@babel/preset-modules" "^0.1.5" - "@babel/types" "^7.18.2" - babel-plugin-polyfill-corejs2 "^0.3.0" - babel-plugin-polyfill-corejs3 "^0.5.0" - babel-plugin-polyfill-regenerator "^0.3.0" - core-js-compat "^3.22.1" - semver "^6.3.0" - -"@babel/preset-modules@^0.1.5": - version "0.1.5" - resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.5.tgz#ef939d6e7f268827e1841638dc6ff95515e115d9" - integrity sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" - "@babel/plugin-transform-dotall-regex" "^7.4.4" - "@babel/types" "^7.4.4" - esutils "^2.0.2" - -"@babel/preset-react@^7.12.0": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.17.12.tgz#62adbd2d1870c0de3893095757ed5b00b492ab3d" - integrity sha512-h5U+rwreXtZaRBEQhW1hOJLMq8XNJBQ/9oymXiCXTuT/0uOwpbT0gUt+sXeOqoXBgNuUKI7TaObVwoEyWkpFgA== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-validator-option" "^7.16.7" - "@babel/plugin-transform-react-display-name" "^7.16.7" - "@babel/plugin-transform-react-jsx" "^7.17.12" - "@babel/plugin-transform-react-jsx-development" "^7.16.7" - "@babel/plugin-transform-react-pure-annotations" "^7.16.7" - -"@babel/runtime@^7.1.2", "@babel/runtime@^7.12.1": - version "7.14.8" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.8.tgz#7119a56f421018852694290b9f9148097391b446" - integrity sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg== - dependencies: - regenerator-runtime "^0.13.4" - -"@babel/runtime@^7.8.4": - version "7.18.3" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.3.tgz#c7b654b57f6f63cf7f8b418ac9ca04408c4579f4" - integrity sha512-38Y8f7YUhce/K7RMwTp7m0uCumpv9hZkitCbBClqQIow1qSbCvGkcegKOXpEWCQLfWmevgRiWokZ1GkpfhbZug== - dependencies: - regenerator-runtime "^0.13.4" - -"@babel/template@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" - integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w== - dependencies: - "@babel/code-frame" "^7.16.7" - "@babel/parser" "^7.16.7" - "@babel/types" "^7.16.7" - -"@babel/traverse@^7.13.0", "@babel/traverse@^7.16.8", "@babel/traverse@^7.18.0", "@babel/traverse@^7.18.2", "@babel/traverse@^7.18.5": - version "7.18.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.5.tgz#94a8195ad9642801837988ab77f36e992d9a20cd" - integrity sha512-aKXj1KT66sBj0vVzk6rEeAO6Z9aiiQ68wfDgge3nHhA/my6xMM/7HGQUNumKZaoa2qUPQ5whJG9aAifsxUKfLA== - dependencies: - "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.18.2" - "@babel/helper-environment-visitor" "^7.18.2" - "@babel/helper-function-name" "^7.17.9" - "@babel/helper-hoist-variables" "^7.16.7" - "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/parser" "^7.18.5" - "@babel/types" "^7.18.4" - debug "^4.1.0" - globals "^11.1.0" - -"@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.17.0", "@babel/types@^7.17.12", "@babel/types@^7.18.0", "@babel/types@^7.18.2", "@babel/types@^7.18.4", "@babel/types@^7.4.4": - version "7.18.4" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.4.tgz#27eae9b9fd18e9dccc3f9d6ad051336f307be354" - integrity sha512-ThN1mBcMq5pG/Vm2IcBmPPfyPXbd8S02rS+OBIDENdufvqC7Z/jHPCv9IcP01277aKtDI8g/2XysBN4hA8niiw== - dependencies: - "@babel/helper-validator-identifier" "^7.16.7" - to-fast-properties "^2.0.0" - -"@jridgewell/gen-mapping@^0.1.0": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" - integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== - dependencies: - "@jridgewell/set-array" "^1.0.0" - "@jridgewell/sourcemap-codec" "^1.4.10" - -"@jridgewell/gen-mapping@^0.3.0": - version "0.3.1" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz#cf92a983c83466b8c0ce9124fadeaf09f7c66ea9" - integrity sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg== - dependencies: - "@jridgewell/set-array" "^1.0.0" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/trace-mapping" "^0.3.9" - -"@jridgewell/resolve-uri@^3.0.3": - version "3.0.7" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz#30cd49820a962aff48c8fffc5cd760151fca61fe" - integrity sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA== - -"@jridgewell/set-array@^1.0.0": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.1.tgz#36a6acc93987adcf0ba50c66908bd0b70de8afea" - integrity sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ== - -"@jridgewell/source-map@^0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb" - integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw== - dependencies: - "@jridgewell/gen-mapping" "^0.3.0" - "@jridgewell/trace-mapping" "^0.3.9" - -"@jridgewell/sourcemap-codec@^1.4.10": - version "1.4.13" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz#b6461fb0c2964356c469e115f504c95ad97ab88c" - integrity sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w== - -"@jridgewell/trace-mapping@^0.3.9": - version "0.3.13" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz#dcfe3e95f224c8fe97a87a5235defec999aa92ea" - integrity sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w== - dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" - -"@lezer/common@^0.15.0", "@lezer/common@^0.15.7": - version "0.15.12" - resolved "https://registry.yarnpkg.com/@lezer/common/-/common-0.15.12.tgz#2f21aec551dd5fd7d24eb069f90f54d5bc6ee5e9" - integrity sha512-edfwCxNLnzq5pBA/yaIhwJ3U3Kz8VAUOTRg0hhxaizaI1N+qxV7EXDv/kLCkLeq2RzSFvxexlaj5Mzfn2kY0Ig== - -"@lezer/lr@^0.15.4": - version "0.15.8" - resolved "https://registry.yarnpkg.com/@lezer/lr/-/lr-0.15.8.tgz#1564a911e62b0a0f75ca63794a6aa8c5dc63db21" - integrity sha512-bM6oE6VQZ6hIFxDNKk8bKPa14hqFrV07J/vHGOeiAbJReIaQXmkVb6xQu4MR+JBTLa5arGRyAAjJe1qaQt3Uvg== - dependencies: - "@lezer/common" "^0.15.0" - -"@mischnic/json-sourcemap@^0.1.0": - version "0.1.0" - resolved "https://registry.yarnpkg.com/@mischnic/json-sourcemap/-/json-sourcemap-0.1.0.tgz#38af657be4108140a548638267d02a2ea3336507" - integrity sha512-dQb3QnfNqmQNYA4nFSN/uLaByIic58gOXq4Y4XqLOWmOrw73KmJPt/HLyG0wvn1bnR6mBKs/Uwvkh+Hns1T0XA== - dependencies: - "@lezer/common" "^0.15.7" - "@lezer/lr" "^0.15.4" - json5 "^2.2.1" - -"@msgpackr-extract/msgpackr-extract-darwin-arm64@2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-2.0.2.tgz#01e3669b8b2dc01f6353f2c87e1ec94faf52c587" - integrity sha512-FMX5i7a+ojIguHpWbzh5MCsCouJkwf4z4ejdUY/fsgB9Vkdak4ZnoIEskOyOUMMB4lctiZFGszFQJXUeFL8tRg== - -"@msgpackr-extract/msgpackr-extract-darwin-x64@2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-2.0.2.tgz#5ca32f16e6f1b7854001a1a2345b61d4e26a0931" - integrity sha512-DznYtF3lHuZDSRaIOYeif4JgO0NtO2Xf8DsngAugMx/bUdTFbg86jDTmkVJBNmV+cxszz6OjGvinnS8AbJ342g== - -"@msgpackr-extract/msgpackr-extract-linux-arm64@2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-2.0.2.tgz#ff629f94379981bf476dffb1439a7c1d3dba2d72" - integrity sha512-b0jMEo566YdM2K+BurSed7bswjo3a6bcdw5ETqoIfSuxKuRLPfAiOjVbZyZBgx3J/TAM/QrvEQ/VN89A0ZAxSg== - -"@msgpackr-extract/msgpackr-extract-linux-arm@2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-2.0.2.tgz#5f6fd30d266c4a90cf989049c7f2e50e5d4fcd4c" - integrity sha512-Gy9+c3Wj+rUlD3YvCZTi92gs+cRX7ZQogtwq0IhRenloTTlsbpezNgk6OCkt59V4ATEWSic9rbU92H/l7XsRvA== - -"@msgpackr-extract/msgpackr-extract-linux-x64@2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-2.0.2.tgz#167faa553b9dbffac8b03bf27de9b6f846f0e1bc" - integrity sha512-zrBHaePwcv4cQXxzYgNj0+A8I1uVN97E7/3LmkRocYZ+rMwUsnPpp4RuTAHSRoKlTQV3nSdCQW4Qdt4MXw/iHw== - -"@msgpackr-extract/msgpackr-extract-win32-x64@2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-2.0.2.tgz#baea7764b1adf201ce4a792fe971fd7211dad2e4" - integrity sha512-fpnI00dt+yO1cKx9qBXelKhPBdEgvc8ZPav1+0r09j0woYQU2N79w/jcGawSY5UGlgQ3vjaJsFHnGbGvvqdLzg== - -"@parcel/bundler-default@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/bundler-default/-/bundler-default-2.6.0.tgz#3b091d2f6ebdb333558fc25c1fb98e28b854ace3" - integrity sha512-AplEdGm/odV7yGmoeOnglxnY31WlNB5EqGLFGxkgs7uwDaTWoTX/9SWPG6xfvirhjDpms8sLSiVuBdFRCCLtNA== - dependencies: - "@parcel/diagnostic" "2.6.0" - "@parcel/hash" "2.6.0" - "@parcel/plugin" "2.6.0" - "@parcel/utils" "2.6.0" - nullthrows "^1.1.1" - -"@parcel/cache@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/cache/-/cache-2.6.0.tgz#19a5132e5715d7ab1df7cb7a5ae5e8c29003a7b1" - integrity sha512-4vbD5uSuf+kRnrFesKhpn0AKnOw8u2UlvCyrplYmp1g9bNAkIooC/nDGdmkb/9SviPEbni9PEanQEHDU2+slpA== - dependencies: - "@parcel/fs" "2.6.0" - "@parcel/logger" "2.6.0" - "@parcel/utils" "2.6.0" - lmdb "2.3.10" - -"@parcel/codeframe@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/codeframe/-/codeframe-2.6.0.tgz#1de477a8772191d5990348b6c75922c1350b835c" - integrity sha512-yXXxrO9yyedHKpTwC+Af0+vPmQm+A9xeEhkt4f0yVg1n4t4yUIxYlTedzbM8ygZEEBtkXU9jJ+PkgXbfMf0dqw== - dependencies: - chalk "^4.1.0" - -"@parcel/compressor-raw@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/compressor-raw/-/compressor-raw-2.6.0.tgz#d8b238db719f43807ebd96ec08270b3c937221e2" - integrity sha512-rtMU2mGl88bic6Xbq1u5L49bMK4s5185b0k7h3JRdS6/0rR+Xp4k/o9Wog+hHjK/s82z1eF9WmET779ZpIDIQQ== - dependencies: - "@parcel/plugin" "2.6.0" - -"@parcel/config-default@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/config-default/-/config-default-2.6.0.tgz#2cc9a05d195a97a93b6e14cbbda96d47d2ccf118" - integrity sha512-DXovFPhZITmTvFaSEdC8RRqROs9FLIJ4u8yFSU6FUyq2wpvtYVRXXoDrvXgClh2csXmK7JTJTp5JF7r0rd2UaA== - dependencies: - "@parcel/bundler-default" "2.6.0" - "@parcel/compressor-raw" "2.6.0" - "@parcel/namer-default" "2.6.0" - "@parcel/optimizer-css" "2.6.0" - "@parcel/optimizer-htmlnano" "2.6.0" - "@parcel/optimizer-image" "2.6.0" - "@parcel/optimizer-svgo" "2.6.0" - "@parcel/optimizer-terser" "2.6.0" - "@parcel/packager-css" "2.6.0" - "@parcel/packager-html" "2.6.0" - "@parcel/packager-js" "2.6.0" - "@parcel/packager-raw" "2.6.0" - "@parcel/packager-svg" "2.6.0" - "@parcel/reporter-dev-server" "2.6.0" - "@parcel/resolver-default" "2.6.0" - "@parcel/runtime-browser-hmr" "2.6.0" - "@parcel/runtime-js" "2.6.0" - "@parcel/runtime-react-refresh" "2.6.0" - "@parcel/runtime-service-worker" "2.6.0" - "@parcel/transformer-babel" "2.6.0" - "@parcel/transformer-css" "2.6.0" - "@parcel/transformer-html" "2.6.0" - "@parcel/transformer-image" "2.6.0" - "@parcel/transformer-js" "2.6.0" - "@parcel/transformer-json" "2.6.0" - "@parcel/transformer-postcss" "2.6.0" - "@parcel/transformer-posthtml" "2.6.0" - "@parcel/transformer-raw" "2.6.0" - "@parcel/transformer-react-refresh-wrap" "2.6.0" - "@parcel/transformer-svg" "2.6.0" - -"@parcel/core@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/core/-/core-2.6.0.tgz#dad1f5f529ffb47df772c155ef09119d3294538c" - integrity sha512-8OOWbPuxpFydpwNyKoz6d3e3O4DmxNYmMw4DXwrPSj/jyg7oa+SDtMT0/VXEhujE0HYkQPCHt4npRajkSuf99A== - dependencies: - "@mischnic/json-sourcemap" "^0.1.0" - "@parcel/cache" "2.6.0" - "@parcel/diagnostic" "2.6.0" - "@parcel/events" "2.6.0" - "@parcel/fs" "2.6.0" - "@parcel/graph" "2.6.0" - "@parcel/hash" "2.6.0" - "@parcel/logger" "2.6.0" - "@parcel/package-manager" "2.6.0" - "@parcel/plugin" "2.6.0" - "@parcel/source-map" "^2.0.0" - "@parcel/types" "2.6.0" - "@parcel/utils" "2.6.0" - "@parcel/workers" "2.6.0" - abortcontroller-polyfill "^1.1.9" - base-x "^3.0.8" - browserslist "^4.6.6" - clone "^2.1.1" - dotenv "^7.0.0" - dotenv-expand "^5.1.0" - json5 "^2.2.0" - msgpackr "^1.5.4" - nullthrows "^1.1.1" - semver "^5.7.1" - -"@parcel/css-darwin-arm64@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@parcel/css-darwin-arm64/-/css-darwin-arm64-1.10.0.tgz#9301fa03f064c16d6a9c23f301566b5943f410bb" - integrity sha512-WMAbjUyCBrXwv3OofNk90K+G0DqZgCFRtKCg+udLXLZCiCe6yrI87ye9SC6KAVwqWp5WT27TPZTrqWJ032e3FA== - -"@parcel/css-darwin-x64@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@parcel/css-darwin-x64/-/css-darwin-x64-1.10.0.tgz#99740212bd1522dafb2c46bebb73603447a262fc" - integrity sha512-p1JJVHOOxrhcSQMq9qlrU88Sl+VJGu8HXBpWDHRzh8aOIkqsiRx1qx9Vl3zGX7Sxnjv/xlPUknLKia8Zy1369A== - -"@parcel/css-linux-arm-gnueabihf@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@parcel/css-linux-arm-gnueabihf/-/css-linux-arm-gnueabihf-1.10.0.tgz#ef961e0c41ef70dc8c58401ccce527bbbbda96f3" - integrity sha512-cUvDN+nNEdoEzZLhOqPAcjICIyEGcFCc0+zJhGKdnA9MC010aeun9ggtToFazIHzMmoF4qyxCY5IyHja8iVkmA== - -"@parcel/css-linux-arm64-gnu@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@parcel/css-linux-arm64-gnu/-/css-linux-arm64-gnu-1.10.0.tgz#7229d05f1f9f76443eb4c9bc55a9e8887c0822dd" - integrity sha512-x8XEtJxgJlstAwbg1BLeYuXhUXEOxGg/BeBFPZr8Zk8dNQ1j1jR7LBk12IKgZrvr+Px1WOFY65lwabgCyFqxnQ== - -"@parcel/css-linux-arm64-musl@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@parcel/css-linux-arm64-musl/-/css-linux-arm64-musl-1.10.0.tgz#d8efb7c09f53971db01ea180a5b63a1b908f1800" - integrity sha512-caBaOM+zhFYlaMB2GL327NeOkF5lbHte5XLrGByagLWanlnRRlFpapIXpuuGIGSF5uBHN2uAz/84ej5mNcdHwg== - -"@parcel/css-linux-x64-gnu@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@parcel/css-linux-x64-gnu/-/css-linux-x64-gnu-1.10.0.tgz#f0e47d1c362ed9b1604b31a1113549930154c959" - integrity sha512-9JZUMB1v+Zh95K2BJdoC20vZcObqF3mPA10gM51/a44f3rhRsv/EHjzLsSqxSYtC+L7wLvW9M3SNZ2KTo0J2/A== - -"@parcel/css-linux-x64-musl@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@parcel/css-linux-x64-musl/-/css-linux-x64-musl-1.10.0.tgz#f83cbfc5b6ee088e9b733b2445131e557934945e" - integrity sha512-U702L0HlZUN5Fxb6jbDetYeA7eOgLHkXo4vZ9/XHJyPy6jD+n+9HO8bEcLdSAadJcb4Ndcn89THyfwKiOHukVQ== - -"@parcel/css-win32-x64-msvc@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@parcel/css-win32-x64-msvc/-/css-win32-x64-msvc-1.10.0.tgz#d2418f657f32913ae685956f3517450fb50fb030" - integrity sha512-44GtojxQBRf8yTetsNdjYSa2KL4/UpSbEeaOYcO+PKBGHcCyQX2Lex5r1X2pXkpNxvu142+dSTLeXhBSFG4C0g== - -"@parcel/css@^1.9.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@parcel/css/-/css-1.10.0.tgz#a2a57e44ac86bc815b955eb605b74b8fc06ac770" - integrity sha512-YvlUqJ3kg/HxsVvq02bTCGruQKjwPEMWEqdyhgfR3aagt+1ibmafy3m8CGYHXvhaQeNYSkMvy1D9bcddFuYTUg== - dependencies: - detect-libc "^1.0.3" - optionalDependencies: - "@parcel/css-darwin-arm64" "1.10.0" - "@parcel/css-darwin-x64" "1.10.0" - "@parcel/css-linux-arm-gnueabihf" "1.10.0" - "@parcel/css-linux-arm64-gnu" "1.10.0" - "@parcel/css-linux-arm64-musl" "1.10.0" - "@parcel/css-linux-x64-gnu" "1.10.0" - "@parcel/css-linux-x64-musl" "1.10.0" - "@parcel/css-win32-x64-msvc" "1.10.0" - -"@parcel/diagnostic@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/diagnostic/-/diagnostic-2.6.0.tgz#99570b28ed44d64d57a3c3521bcfa4f6f631b495" - integrity sha512-+p8gC2FKxSI2veD7SoaNlP572v4kw+nafCQEPDtJuzYYRqywYUGncch25dkpgNApB4W4cXVkZu3ZbtIpCAmjQQ== - dependencies: - "@mischnic/json-sourcemap" "^0.1.0" - nullthrows "^1.1.1" - -"@parcel/events@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/events/-/events-2.6.0.tgz#6066c8c7b320e12fd206877bd549825b7eea8c63" - integrity sha512-2WaKtBs4iYwS88j4zRdyTJTgh8iuY4E32FMmjzzbheqETs6I05gWuPReGukJYxk8vc0Ir7tbzp12oAfpgo0Y+g== - -"@parcel/fs-search@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/fs-search/-/fs-search-2.6.0.tgz#35c52da3186cab953cf6686304921a7ab0c81be8" - integrity sha512-1nXzM3H/cA4kzLKvDBvwmNisKCdRqlgkLXh+OR1Zu28Kn4W34KuJMcHWW8cC+WIuuKqDh5oo2WPsC5y65GXBKQ== - dependencies: - detect-libc "^1.0.3" - -"@parcel/fs@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/fs/-/fs-2.6.0.tgz#287a3cda558f16aae5c67ccbe33a17c1bbd75ceb" - integrity sha512-6vxtx5Zy6MvDvH1EPx9JxjKGF03bR7VE1dUf4HLeX2D8YmpL5hkHJnlRCFdcH08rzOVwaJLzg1QNtblWJXQ9CA== - dependencies: - "@parcel/fs-search" "2.6.0" - "@parcel/types" "2.6.0" - "@parcel/utils" "2.6.0" - "@parcel/watcher" "^2.0.0" - "@parcel/workers" "2.6.0" - -"@parcel/graph@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/graph/-/graph-2.6.0.tgz#04f9660333e314a51af38483efefd766a5841bb0" - integrity sha512-rxrAzWm6rwbCRPbu0Z+zwMscpG8omffODniVWPlX2G0jgQGpjKsutBQ6RMfFIcfaQ4MzL3pIQOTf8bkjQOPsbg== - dependencies: - "@parcel/utils" "2.6.0" - nullthrows "^1.1.1" - -"@parcel/hash@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/hash/-/hash-2.6.0.tgz#c41364425e08d7e0ae5dae8b49ebfec2094124fe" - integrity sha512-YugWqhLxqK80Lo++3B3Kr5UPCHOdS8iI2zJ1jkzUeH9v6WUzbwWOnmPf6lN2S5m1BrIFFJd8Jc+CbEXWi8zoJA== - dependencies: - detect-libc "^1.0.3" - xxhash-wasm "^0.4.2" - -"@parcel/logger@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/logger/-/logger-2.6.0.tgz#f7aa26368e39573a5362997bb215f4a987c799e4" - integrity sha512-J1/7kPfSGBvMKSZdi0WCNuN0fIeiWxifnDGn7W/K8KhD422YwFJA8N046ps8nkDOPIXf1osnIECNp4GIR9oSYw== - dependencies: - "@parcel/diagnostic" "2.6.0" - "@parcel/events" "2.6.0" - -"@parcel/markdown-ansi@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/markdown-ansi/-/markdown-ansi-2.6.0.tgz#69720735d27ca039e1e03f0277224ec5a99c0ef7" - integrity sha512-fyjkrJQQSfKTUFTTasdZ6WrAkDoQ2+DYDjj+3p+RncYyrIa9zArKx4IiRiipsvNdtMvP0/hTdK8F3BOJ3KSU/g== - dependencies: - chalk "^4.1.0" - -"@parcel/namer-default@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/namer-default/-/namer-default-2.6.0.tgz#1978397aabf13824f433157c683f64e1b6d37936" - integrity sha512-r8O12r7ozJBctnFxVdXbf/fK97GIdNj3hiiUNWlXEmED9sw6ZPcChaLcfot0/443g8i87JDmSTKJ8js2tuz5XA== - dependencies: - "@parcel/diagnostic" "2.6.0" - "@parcel/plugin" "2.6.0" - nullthrows "^1.1.1" - -"@parcel/node-resolver-core@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/node-resolver-core/-/node-resolver-core-2.6.0.tgz#2666897414274e0de72221f8ec34590f029ab95d" - integrity sha512-AJDj5DZbB58plv0li8bdVSD+zpnkHE36Om3TYyNn1jgXXwgBM64Er/9p8yQn356jBqTQMh7zlJqvbdIyOiMeMg== - dependencies: - "@parcel/diagnostic" "2.6.0" - "@parcel/utils" "2.6.0" - nullthrows "^1.1.1" - -"@parcel/optimizer-css@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/optimizer-css/-/optimizer-css-2.6.0.tgz#ae48b468d528f19a10e64b588dff8859aa31a15d" - integrity sha512-VMJknUwfKCw6Woov0lnPGdsGZewcI4ghW8WKmNZzC5uKCetk1XetV55QHBc1RNjGfsjfSTZiSa3guATj2zFJkQ== - dependencies: - "@parcel/css" "^1.9.0" - "@parcel/diagnostic" "2.6.0" - "@parcel/plugin" "2.6.0" - "@parcel/source-map" "^2.0.0" - "@parcel/utils" "2.6.0" - browserslist "^4.6.6" - nullthrows "^1.1.1" - -"@parcel/optimizer-htmlnano@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/optimizer-htmlnano/-/optimizer-htmlnano-2.6.0.tgz#ad1775de73a338e19bfbb4c43ccda2a8eb99433d" - integrity sha512-HmvcUoYpfdx8ZfID4WOj/SE8N78NEBmzAffZ8f827mYMr4ZrbKzAgg6OG3tBbfF0zxH0bIjZcwqwZYk4SdbG7g== - dependencies: - "@parcel/plugin" "2.6.0" - htmlnano "^2.0.0" - nullthrows "^1.1.1" - posthtml "^0.16.5" - svgo "^2.4.0" - -"@parcel/optimizer-image@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/optimizer-image/-/optimizer-image-2.6.0.tgz#2dca41db64b51fa5d5a463278851c729d9ff1d8c" - integrity sha512-FDNr3LJ8SWR9rrtdCrZOlYF1hE9G5pxUWawGxUasbvqwcY5lEQwr2KRmfGZeg+KwOnzlImlY6dP2LGox1NFddQ== - dependencies: - "@parcel/diagnostic" "2.6.0" - "@parcel/plugin" "2.6.0" - "@parcel/utils" "2.6.0" - "@parcel/workers" "2.6.0" - detect-libc "^1.0.3" - -"@parcel/optimizer-svgo@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/optimizer-svgo/-/optimizer-svgo-2.6.0.tgz#6ed5e6bd50dd8b432818bf61881a2b602e1cc394" - integrity sha512-LMTDVMd7T/IfLG59yLWl8Uw2HYGbj2C3jIwkMqH9MBUT5KILK66T3t0yV86SoZJnxZ6xBIJ+kCcCRssCzhvanw== - dependencies: - "@parcel/diagnostic" "2.6.0" - "@parcel/plugin" "2.6.0" - "@parcel/utils" "2.6.0" - svgo "^2.4.0" - -"@parcel/optimizer-terser@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/optimizer-terser/-/optimizer-terser-2.6.0.tgz#98179f7e5e4c74f80aaca7660d4a83d0ed03877e" - integrity sha512-oezRt6Lz/QqcVDXyMfFjzQc7n0ThJowLJ4Lyhu8rMh0ZJYzc4UCFCw/19d4nRnzE+Qg0vj3mQCpdkA9/64E44g== - dependencies: - "@parcel/diagnostic" "2.6.0" - "@parcel/plugin" "2.6.0" - "@parcel/source-map" "^2.0.0" - "@parcel/utils" "2.6.0" - nullthrows "^1.1.1" - terser "^5.2.0" - -"@parcel/package-manager@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/package-manager/-/package-manager-2.6.0.tgz#2d5dca646f2484ff6d643e1a2ed88cc48b25c6f6" - integrity sha512-AqFfdkbOw51q/3ia2mIsFTmrpYEyUb3k+2uYC5GsLMz3go6OGn7/Crz0lZLSclv5EtwpRg3TWr9yL7RekVN/Uw== - dependencies: - "@parcel/diagnostic" "2.6.0" - "@parcel/fs" "2.6.0" - "@parcel/logger" "2.6.0" - "@parcel/types" "2.6.0" - "@parcel/utils" "2.6.0" - "@parcel/workers" "2.6.0" - semver "^5.7.1" - -"@parcel/packager-css@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/packager-css/-/packager-css-2.6.0.tgz#b3dff55c1a3d65bd359d1dd3753c25ea7ea44d62" - integrity sha512-iXUttSe+wtnIM2PKCyFC2I4+Szv+8qHpC3wXeJlXlzd8wljm42y+6Fs4FZ0zihTccRxI2UUhFnKu90ag+5AmjA== - dependencies: - "@parcel/plugin" "2.6.0" - "@parcel/source-map" "^2.0.0" - "@parcel/utils" "2.6.0" - nullthrows "^1.1.1" - -"@parcel/packager-html@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/packager-html/-/packager-html-2.6.0.tgz#a23b268cac7f4a8841f3f6c570a00fd4fe9ca859" - integrity sha512-HsiXMkU9AJr3LLjsP2Kteho2jCVpabTwcU/fauwbwirhg0xNlRsKxYZRCllRhPkb0FWAnkjzwjOj01MHD6NJCg== - dependencies: - "@parcel/plugin" "2.6.0" - "@parcel/types" "2.6.0" - "@parcel/utils" "2.6.0" - nullthrows "^1.1.1" - posthtml "^0.16.5" - -"@parcel/packager-js@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/packager-js/-/packager-js-2.6.0.tgz#31810f10de497dd67e1912f83de0aba66db58173" - integrity sha512-Uz3pqIFchFfKszWnNGDgIwM1uwHHJp7Dts6VzS9lf/2RbRgZT0fmce+NPgnVO5MMKBHzdvm32ShT6gFAABF5Vw== - dependencies: - "@parcel/diagnostic" "2.6.0" - "@parcel/hash" "2.6.0" - "@parcel/plugin" "2.6.0" - "@parcel/source-map" "^2.0.0" - "@parcel/utils" "2.6.0" - globals "^13.2.0" - nullthrows "^1.1.1" - -"@parcel/packager-raw@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/packager-raw/-/packager-raw-2.6.0.tgz#8ccf041acd102a38b2ffa02fd8ef634652255bd2" - integrity sha512-ktT6Qc/GgCq8H1+6y+AXufVzQj1s6KRoKf83qswCD0iY3MwCbJoEfc3IsB4K64FpHIL5Eu0z54IId+INvGbOYA== - dependencies: - "@parcel/plugin" "2.6.0" - -"@parcel/packager-svg@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/packager-svg/-/packager-svg-2.6.0.tgz#676a3ba95259deece6cd9826f1a677cbbf1d2ea3" - integrity sha512-OF2RShyspXu7H4Dn2PmchfMMYPx+kWjOXiYVQ6OkOI0MZmOydx7p8nrcG5+y7vCJTPlta828BSwva0GdKfn46A== - dependencies: - "@parcel/plugin" "2.6.0" - "@parcel/types" "2.6.0" - "@parcel/utils" "2.6.0" - posthtml "^0.16.4" - -"@parcel/plugin@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/plugin/-/plugin-2.6.0.tgz#84fd9fffd7891027e4040be4b94647652fd46354" - integrity sha512-LzOaiK8R6eFEoov1cb3/W+o0XvXdI/VbDhMDl0L0II+/56M0UeayYtFP5QGTDn/fZqVlYfzPCtt3EMwdG7/dow== - dependencies: - "@parcel/types" "2.6.0" - -"@parcel/reporter-cli@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/reporter-cli/-/reporter-cli-2.6.0.tgz#09fe5a8eecc368f2bcaaf6ab8154378bab0e0242" - integrity sha512-QFG957NXx3L0D8Zw0+B2j7IHy8f/UzOVu6VvKE3rMkhq/iR2qLrPohQ+uvxlee+CLC0cG2qRSgJ7Ve/rjQPoJg== - dependencies: - "@parcel/plugin" "2.6.0" - "@parcel/types" "2.6.0" - "@parcel/utils" "2.6.0" - chalk "^4.1.0" - term-size "^2.2.1" - -"@parcel/reporter-dev-server@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/reporter-dev-server/-/reporter-dev-server-2.6.0.tgz#8e692916c6684c3c04fecef058ddddae6b74121c" - integrity sha512-VvygsCA+uzWyijIV8zqU1gFyhAWknuaY4KIWhV4kCT8afRJwsLSwt/tpdaKDPuPU45h3tTsUdXH1wjaIk+dGeQ== - dependencies: - "@parcel/plugin" "2.6.0" - "@parcel/utils" "2.6.0" - -"@parcel/resolver-default@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/resolver-default/-/resolver-default-2.6.0.tgz#a80bc39c402abe0e78e3de8997ca2ea636c28a91" - integrity sha512-ATk9wXvy5GOHAqyHbnCnU11fUPTtf8dLjpgVqL5XylwugZnyBXbynoTWX4w8h6mffkVtdfmzTJx/o4Lresz9sA== - dependencies: - "@parcel/node-resolver-core" "2.6.0" - "@parcel/plugin" "2.6.0" - -"@parcel/runtime-browser-hmr@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/runtime-browser-hmr/-/runtime-browser-hmr-2.6.0.tgz#e817ead910f9ba572ed8f477447c862acbfe8d73" - integrity sha512-90xvv/10cFML5dAhClBEJZ/ExiBQVPqQsZcvRmVZmc5mpZVJMKattWCQrd7pAf7FDYl4JAcvsK3DTwvRT/oLNA== - dependencies: - "@parcel/plugin" "2.6.0" - "@parcel/utils" "2.6.0" - -"@parcel/runtime-js@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/runtime-js/-/runtime-js-2.6.0.tgz#a10c672c7f90360d5903180d0e2b808355708e80" - integrity sha512-R4tJAIT/SX7VBQ+f7WmeekREQzzLsmgP1j486uKhQNyYrpvsN0HnRbg5aqvZjEjkEmSeJR0mOlWtMK5/m+0yTA== - dependencies: - "@parcel/plugin" "2.6.0" - "@parcel/utils" "2.6.0" - nullthrows "^1.1.1" - -"@parcel/runtime-react-refresh@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/runtime-react-refresh/-/runtime-react-refresh-2.6.0.tgz#52ae4d9acba3e1e3b20a4f2712ea140fac21aaaf" - integrity sha512-2sRd13gc2EbMV/O5n2NPVGGhKBasb1fDTXGEY8y7qi9xDKc+ewok/D83T+w243FhCPS9Pf3ur5GkbPlrJGcenQ== - dependencies: - "@parcel/plugin" "2.6.0" - "@parcel/utils" "2.6.0" - react-error-overlay "6.0.9" - react-refresh "^0.9.0" - -"@parcel/runtime-service-worker@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/runtime-service-worker/-/runtime-service-worker-2.6.0.tgz#10e90d02d83ebe763bb8de838a8f03eb3118aef9" - integrity sha512-nVlknGw5J5Bkd1Wr1TbyWHhUd9CmVVebaRg/lpfVKYhAuE/2r+3N0+J8qbEIgtTRcHaSV7wTNpg4weSWq46VeA== - dependencies: - "@parcel/plugin" "2.6.0" - "@parcel/utils" "2.6.0" - nullthrows "^1.1.1" - -"@parcel/source-map@^2.0.0": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@parcel/source-map/-/source-map-2.0.5.tgz#7dabcea0890914c390b8cf32e208d3f667e1ff38" - integrity sha512-DRVlCFKLpqBSIbMxUoVlHgfiv12HTW/U7nnhzw52YgzDVXUX9OA41dXS1PU0pJ1si+D1k8msATUC+AoldN43mg== - dependencies: - detect-libc "^1.0.3" - -"@parcel/transformer-babel@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/transformer-babel/-/transformer-babel-2.6.0.tgz#f47454438b2beae348f3caf9aecc4619f81759f1" - integrity sha512-qTDzhLoaTpRJoppCNqaAlcUYxcDEvJffem1h3SAQiwvCLUBQowLyeaBy8sUxu54AU6eHFJyBld5ZocENyHTBCA== - dependencies: - "@parcel/diagnostic" "2.6.0" - "@parcel/plugin" "2.6.0" - "@parcel/source-map" "^2.0.0" - "@parcel/utils" "2.6.0" - browserslist "^4.6.6" - json5 "^2.2.0" - nullthrows "^1.1.1" - semver "^5.7.0" - -"@parcel/transformer-css@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/transformer-css/-/transformer-css-2.6.0.tgz#6b91cb23d569f9ce9eed39e30d965c92baf54720" - integrity sha512-Ei9NPE5Rl9V+MGd8qddfZD0Fsqbvky8J62RwYsqLkptFl9FkhgwOu8Cmokz7IIc4GJ2qzfnG5y54K/Bi7Moq4Q== - dependencies: - "@parcel/css" "^1.9.0" - "@parcel/diagnostic" "2.6.0" - "@parcel/plugin" "2.6.0" - "@parcel/source-map" "^2.0.0" - "@parcel/utils" "2.6.0" - browserslist "^4.6.6" - nullthrows "^1.1.1" - -"@parcel/transformer-html@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/transformer-html/-/transformer-html-2.6.0.tgz#47d0deed12d83ea0286baf65a87b00e47a686674" - integrity sha512-YQh5WzNFjPhgV09P+zVS++albTCTvbPYAJXp5zUJ4HavzcpV2IB3HAPRk9x+iXUeRBQYYiO5SMMRkdy9a4CzQQ== - dependencies: - "@parcel/diagnostic" "2.6.0" - "@parcel/hash" "2.6.0" - "@parcel/plugin" "2.6.0" - nullthrows "^1.1.1" - posthtml "^0.16.5" - posthtml-parser "^0.10.1" - posthtml-render "^3.0.0" - semver "^5.7.1" - -"@parcel/transformer-image@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/transformer-image/-/transformer-image-2.6.0.tgz#db754a00670dbfaaa22b372017d827e009da9513" - integrity sha512-Zkh1i6nWNOTOReKlZD+bLJCHA16dPLO6Or7ETAHtSF3iRzMNFcVFp+851Awj3l4zeJ6CoCWlyxsR4CEdioRgiQ== - dependencies: - "@parcel/plugin" "2.6.0" - "@parcel/workers" "2.6.0" - nullthrows "^1.1.1" - -"@parcel/transformer-js@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/transformer-js/-/transformer-js-2.6.0.tgz#b9f297e391a7091aaf0432135cd7f6c86e76301b" - integrity sha512-4v2r3EVdMKowBziVBW9HZqvAv88HaeiezkWyMX4wAfplo9jBtWEp99KEQINzSEdbXROR81M9oJjlGF5+yoVr/w== - dependencies: - "@parcel/diagnostic" "2.6.0" - "@parcel/plugin" "2.6.0" - "@parcel/source-map" "^2.0.0" - "@parcel/utils" "2.6.0" - "@parcel/workers" "2.6.0" - "@swc/helpers" "^0.3.15" - browserslist "^4.6.6" - detect-libc "^1.0.3" - nullthrows "^1.1.1" - regenerator-runtime "^0.13.7" - semver "^5.7.1" - -"@parcel/transformer-json@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/transformer-json/-/transformer-json-2.6.0.tgz#c15fc774431bab4c7059b4013e0d1ca9b66fad5c" - integrity sha512-zb+TQAdHWdXijKcFhLe+5KN1O0IzXwW1gJhPr8DJEA3qhPaCsncsw5RCVjQlP3a7NXr1mMm1eMtO6bhIMqbXeA== - dependencies: - "@parcel/plugin" "2.6.0" - json5 "^2.2.0" - -"@parcel/transformer-postcss@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/transformer-postcss/-/transformer-postcss-2.6.0.tgz#4ff590a60fb71244bc7165326c73eaa39dc70f93" - integrity sha512-czmh2mOPJLwYbtnPTFlxKYcaQHH6huIlpfNX1XgdsaEYS+yFs8ZXpzqjxI1wu6rMW0R0q5aon72yB3PJewvqNQ== - dependencies: - "@parcel/diagnostic" "2.6.0" - "@parcel/hash" "2.6.0" - "@parcel/plugin" "2.6.0" - "@parcel/utils" "2.6.0" - clone "^2.1.1" - nullthrows "^1.1.1" - postcss-value-parser "^4.2.0" - semver "^5.7.1" - -"@parcel/transformer-posthtml@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/transformer-posthtml/-/transformer-posthtml-2.6.0.tgz#6080b59d2de05d212fe7bfd18af5724e824bca66" - integrity sha512-R1FmPMZ0pgrbPZkDppa2pE+6KDK3Wxof6uQo7juHLB2ELGOTaYofsG3nrRdk+chyAHaVv4qWLqXbfZK6pGepEg== - dependencies: - "@parcel/plugin" "2.6.0" - "@parcel/utils" "2.6.0" - nullthrows "^1.1.1" - posthtml "^0.16.5" - posthtml-parser "^0.10.1" - posthtml-render "^3.0.0" - semver "^5.7.1" - -"@parcel/transformer-raw@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/transformer-raw/-/transformer-raw-2.6.0.tgz#c0648e0f788bc71a26235788edc662f303f7c91b" - integrity sha512-QDirlWCS/qy0DQ3WvDIAnFP52n1TJW/uWH+4PGMNnX4/M3/2UchY8xp9CN0tx4NQ4g09S8o3gLlHvNxQqZxFrQ== - dependencies: - "@parcel/plugin" "2.6.0" - -"@parcel/transformer-react-refresh-wrap@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/transformer-react-refresh-wrap/-/transformer-react-refresh-wrap-2.6.0.tgz#8a3c2274549189c04440562ae4d3ca17ac4a861a" - integrity sha512-G34orfvLDUTumuerqNmA8T8NUHk+R0jwUjbVPO7gpB6VCVQ5ocTABdE9vN9Uu/cUsHij40TUFwqK4R9TFEBIEQ== - dependencies: - "@parcel/plugin" "2.6.0" - "@parcel/utils" "2.6.0" - react-refresh "^0.9.0" - -"@parcel/transformer-svg@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/transformer-svg/-/transformer-svg-2.6.0.tgz#ca307c7309787c1b4180ae4742f33edc5fc073d0" - integrity sha512-e7yrb7775A7tEGRsAHQSMhXe+u4yisH5W0PuIzAQQy/a2IwBjaSxNnvyelN7tNX0FYq0BK6An5wRbhK4YmM+xw== - dependencies: - "@parcel/diagnostic" "2.6.0" - "@parcel/hash" "2.6.0" - "@parcel/plugin" "2.6.0" - nullthrows "^1.1.1" - posthtml "^0.16.5" - posthtml-parser "^0.10.1" - posthtml-render "^3.0.0" - semver "^5.7.1" - -"@parcel/types@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/types/-/types-2.6.0.tgz#b9b7f93edaafcb77425e231a0b4662d3c8d61900" - integrity sha512-lAMYvOBfNEJMsPJ+plbB50305o0TwNrY1xX5RRIWBqwOa6bYmbW1ZljUk1tQvnkpIE4eAHQwnPR5Z2XWg18wGQ== - dependencies: - "@parcel/cache" "2.6.0" - "@parcel/diagnostic" "2.6.0" - "@parcel/fs" "2.6.0" - "@parcel/package-manager" "2.6.0" - "@parcel/source-map" "^2.0.0" - "@parcel/workers" "2.6.0" - utility-types "^3.10.0" - -"@parcel/utils@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/utils/-/utils-2.6.0.tgz#d2d42635ad5b398fa21b26940868e7ff30175c07" - integrity sha512-ElXz+QHtT1JQIucbQJBk7SzAGoOlBp4yodEQVvTKS7GA+hEGrSP/cmibl6qm29Rjtd0zgQsdd+2XmP3xvP2gQQ== - dependencies: - "@parcel/codeframe" "2.6.0" - "@parcel/diagnostic" "2.6.0" - "@parcel/hash" "2.6.0" - "@parcel/logger" "2.6.0" - "@parcel/markdown-ansi" "2.6.0" - "@parcel/source-map" "^2.0.0" - chalk "^4.1.0" - -"@parcel/watcher@^2.0.0": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.0.5.tgz#f913a54e1601b0aac972803829b0eece48de215b" - integrity sha512-x0hUbjv891omnkcHD7ZOhiyyUqUUR6MNjq89JhEI3BxppeKWAm6NPQsqqRrAkCJBogdT/o/My21sXtTI9rJIsw== - dependencies: - node-addon-api "^3.2.1" - node-gyp-build "^4.3.0" - -"@parcel/workers@2.6.0": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@parcel/workers/-/workers-2.6.0.tgz#09a53d62425d26eb1ee288371348c4dedf0347c9" - integrity sha512-3tcI2LF5fd/WZtSnSjyWdDE+G+FitdNrRgSObzSp+axHKMAM23sO0z7KY8s2SYCF40msdYbFUW8eI6JlYNJoWQ== - dependencies: - "@parcel/diagnostic" "2.6.0" - "@parcel/logger" "2.6.0" - "@parcel/types" "2.6.0" - "@parcel/utils" "2.6.0" - chrome-trace-event "^1.0.2" - nullthrows "^1.1.1" - -"@swc/helpers@^0.3.15": - version "0.3.17" - resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.3.17.tgz#7c1b91f43c77e2bba99492162a498d465ef253d5" - integrity sha512-tb7Iu+oZ+zWJZ3HJqwx8oNwSDIU440hmVMDPhpACWQWnrZHK99Bxs70gT1L2dnr5Hg50ZRWEFkQCAnOVVV0z1Q== - dependencies: - tslib "^2.4.0" - -"@trysound/sax@0.2.0": - version "0.2.0" - resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" - integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== - -"@types/parse-json@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" - integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== - -abortcontroller-polyfill@^1.1.9: - version "1.7.3" - resolved "https://registry.yarnpkg.com/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.3.tgz#1b5b487bd6436b5b764fd52a612509702c3144b5" - integrity sha512-zetDJxd89y3X99Kvo4qFx8GKlt6GsvN3UcRZHwU6iFA/0KiOmhkTVhe8oRoTBiTVPZu09x3vCra47+w8Yz1+2Q== - -ace-builds@^1.4.12: - version "1.4.12" - resolved "https://registry.yarnpkg.com/ace-builds/-/ace-builds-1.4.12.tgz#888efa386e36f4345f40b5233fcc4fe4c588fae7" - integrity sha512-G+chJctFPiiLGvs3+/Mly3apXTcfgE45dT5yp12BcWZ1kUs+gm0qd3/fv4gsz6fVag4mM0moHVpjHDIgph6Psg== - -acorn@^8.5.0: - version "8.7.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30" - integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A== - -ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - dependencies: - color-convert "^1.9.0" - -ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -babel-plugin-dynamic-import-node@^2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" - integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== - dependencies: - object.assign "^4.1.0" - -babel-plugin-polyfill-corejs2@^0.3.0: - version "0.3.1" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz#440f1b70ccfaabc6b676d196239b138f8a2cfba5" - integrity sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w== - dependencies: - "@babel/compat-data" "^7.13.11" - "@babel/helper-define-polyfill-provider" "^0.3.1" - semver "^6.1.1" - -babel-plugin-polyfill-corejs3@^0.5.0: - version "0.5.2" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz#aabe4b2fa04a6e038b688c5e55d44e78cd3a5f72" - integrity sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ== - dependencies: - "@babel/helper-define-polyfill-provider" "^0.3.1" - core-js-compat "^3.21.0" - -babel-plugin-polyfill-regenerator@^0.3.0: - version "0.3.1" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz#2c0678ea47c75c8cc2fbb1852278d8fb68233990" - integrity sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A== - dependencies: - "@babel/helper-define-polyfill-provider" "^0.3.1" - -base-x@^3.0.8: - version "3.0.9" - resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.9.tgz#6349aaabb58526332de9f60995e548a53fe21320" - integrity sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ== - dependencies: - safe-buffer "^5.0.1" - -boolbase@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" - -bootstrap@^4.1.1: - version "4.1.3" - resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.1.3.tgz#0eb371af2c8448e8c210411d0cb824a6409a12be" - -bootswatch@^4.1.1: - version "4.1.3" - resolved "https://registry.yarnpkg.com/bootswatch/-/bootswatch-4.1.3.tgz#30e3723b19fa89568cffcdc9c8943b469a2745e9" - -browserslist@^4.20.2, browserslist@^4.20.4, browserslist@^4.6.6: - version "4.20.4" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.20.4.tgz#98096c9042af689ee1e0271333dbc564b8ce4477" - integrity sha512-ok1d+1WpnU24XYN7oC3QWgTyMhY/avPJ/r9T00xxvUOIparA/gc+UPUMaod3i+G6s+nI2nUb9xZ5k794uIwShw== - dependencies: - caniuse-lite "^1.0.30001349" - electron-to-chromium "^1.4.147" - escalade "^3.1.1" - node-releases "^2.0.5" - picocolors "^1.0.0" - -buffer-from@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" - -call-bind@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== - dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.2" - -callsites@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" - integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== - -caniuse-lite@^1.0.30001349: - version "1.0.30001352" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001352.tgz#cc6f5da3f983979ad1e2cdbae0505dccaa7c6a12" - integrity sha512-GUgH8w6YergqPQDGWhJGt8GDRnY0L/iJVQcU3eJ46GYf52R8tk0Wxp0PymuFVZboJYXGiCqwozAYZNRjVj6IcA== - -chalk@^2.0.0: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chalk@^4.1.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chrome-trace-event@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" - integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== - -classnames@^2.2.3, classnames@^2.2.5: - version "2.2.6" - resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce" - -classnames@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e" - integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA== - -clone@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" - -color-convert@^1.9.0: - version "1.9.2" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.2.tgz#49881b8fba67df12a96bdf3f56c0aab9e7913147" - dependencies: - color-name "1.1.1" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.1.tgz#4b1415304cf50028ea81643643bd82ea05803689" - -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -commander@^2.20.0: - version "2.20.3" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" - integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== - -commander@^7.0.0, commander@^7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" - integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== - -convert-source-map@^1.7.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" - integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== - dependencies: - safe-buffer "~5.1.1" - -core-js-compat@^3.21.0, core-js-compat@^3.22.1: - version "3.23.0" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.23.0.tgz#324191a23bfbd229802718dcc1d213414b244624" - integrity sha512-i4FgbtahOArZBEteiL+czI5N/bp17w16bXmLagGThdA2zuX1a5X4HbBmOVD7ERRtk3wMtPOFEmlXpVV4lsvwNw== - dependencies: - browserslist "^4.20.4" - semver "7.0.0" - -core-js@^2.5.3: - version "2.5.7" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e" - -cosmiconfig@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d" - integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ== - dependencies: - "@types/parse-json" "^4.0.0" - import-fresh "^3.2.1" - parse-json "^5.0.0" - path-type "^4.0.0" - yaml "^1.10.0" - -css-select@^4.1.3: - version "4.3.0" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b" - integrity sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ== - dependencies: - boolbase "^1.0.0" - css-what "^6.0.1" - domhandler "^4.3.1" - domutils "^2.8.0" - nth-check "^2.0.1" - -css-tree@^1.1.2, css-tree@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" - integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== - dependencies: - mdn-data "2.0.14" - source-map "^0.6.1" - -css-what@^6.0.1: - version "6.1.0" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" - integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== - -csso@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529" - integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA== - dependencies: - css-tree "^1.1.2" - -debug@^4.1.0, debug@^4.1.1: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== - dependencies: - ms "2.1.2" - -define-properties@^1.1.3: - version "1.1.4" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" - integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== - dependencies: - has-property-descriptors "^1.0.0" - object-keys "^1.1.1" - -detect-libc@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" - integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg== - -diff-match-patch@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/diff-match-patch/-/diff-match-patch-1.0.5.tgz#abb584d5f10cd1196dfc55aa03701592ae3f7b37" - integrity sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw== - -dom-helpers@^3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.3.1.tgz#fc1a4e15ffdf60ddde03a480a9c0fece821dd4a6" - -dom-serializer@^1.0.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30" - integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag== - dependencies: - domelementtype "^2.0.1" - domhandler "^4.2.0" - entities "^2.0.0" - -domelementtype@^2.0.1, domelementtype@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" - integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== - -domhandler@^4.2.0, domhandler@^4.2.2, domhandler@^4.3.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c" - integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ== - dependencies: - domelementtype "^2.2.0" - -domutils@^2.8.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" - integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== - dependencies: - dom-serializer "^1.0.1" - domelementtype "^2.2.0" - domhandler "^4.2.0" - -dotenv-expand@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0" - integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA== - -dotenv@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-7.0.0.tgz#a2be3cd52736673206e8a85fb5210eea29628e7c" - integrity sha512-M3NhsLbV1i6HuGzBUH8vXrtxOk+tWmzWKDMbAVSUp3Zsjm7ywFeuwrUXhmhQyRK1q5B5GGy7hcXPbj3bnfZg2g== - -dygraphs@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/dygraphs/-/dygraphs-2.1.0.tgz#2fbfd2c803ead02307df3faf8d4dd3ef55cb2075" - -electron-to-chromium@^1.4.147: - version "1.4.154" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.154.tgz#d69c60499fc467a6c59591d29183e520afbc78a1" - integrity sha512-GbV9djOkrnj6xmW+YYVVEI3VCQnJ0pnSTu7TW2JyjKd5cakoiSaG5R4RbEtfaD92GsY10DzbU3GYRe+IOA9kqA== - -entities@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" - integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== - -entities@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4" - integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q== - -error-ex@^1.3.1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" - dependencies: - is-arrayish "^0.2.1" - -escalade@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== - -escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - -esutils@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" - -feather-icons@^4.7.3: - version "4.7.3" - resolved "https://registry.yarnpkg.com/feather-icons/-/feather-icons-4.7.3.tgz#c33d95f2df4582f26a0bb4c11f281fe7faec12aa" - dependencies: - classnames "^2.2.5" - core-js "^2.5.3" - -font-awesome@^4.7.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/font-awesome/-/font-awesome-4.7.0.tgz#8fa8cf0411a1a31afd07b06d2902bb9fc815a133" - -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== - -gensync@^1.0.0-beta.2: - version "1.0.0-beta.2" - resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" - integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== - -get-intrinsic@^1.0.2, get-intrinsic@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.2.tgz#336975123e05ad0b7ba41f152ee4aadbea6cf598" - integrity sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA== - dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.3" - -get-port@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/get-port/-/get-port-4.2.0.tgz#e37368b1e863b7629c43c5a323625f95cf24b119" - integrity sha512-/b3jarXkH8KJoOMQc3uVGHASwGLPq3gSFJ7tgJm2diza+bydJPTGOibin2steecKeOylE8oY2JERlVWkAJO6yw== - -globals@^11.1.0: - version "11.12.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" - integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== - -globals@^13.2.0: - version "13.15.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.15.0.tgz#38113218c907d2f7e98658af246cef8b77e90bac" - integrity sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog== - dependencies: - type-fest "^0.20.2" - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -has-property-descriptors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" - integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== - dependencies: - get-intrinsic "^1.1.1" - -has-symbols@^1.0.1, has-symbols@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" - integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== - -has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== - dependencies: - function-bind "^1.1.1" - -history@^4.9.0: - version "4.10.1" - resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3" - integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew== - dependencies: - "@babel/runtime" "^7.1.2" - loose-envify "^1.2.0" - resolve-pathname "^3.0.0" - tiny-invariant "^1.0.2" - tiny-warning "^1.0.0" - value-equal "^1.0.1" - -hoist-non-react-statics@^3.1.0: - version "3.3.2" - resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" - integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== - dependencies: - react-is "^16.7.0" - -htmlnano@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/htmlnano/-/htmlnano-2.0.2.tgz#3e3170941e2446a86211196d740272ebca78f878" - integrity sha512-+ZrQFS4Ub+zd+/fWwfvoYCEGNEa0/zrpys6CyXxvZDwtL7Pl+pOtRkiujyvBQ7Lmfp7/iEPxtOFgxWA16Gkj3w== - dependencies: - cosmiconfig "^7.0.1" - posthtml "^0.16.5" - timsort "^0.3.0" - -htmlparser2@^7.1.1: - version "7.2.0" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-7.2.0.tgz#8817cdea38bbc324392a90b1990908e81a65f5a5" - integrity sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog== - dependencies: - domelementtype "^2.0.1" - domhandler "^4.2.2" - domutils "^2.8.0" - entities "^3.0.1" - -import-fresh@^3.2.1: - version "3.3.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" - integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== - dependencies: - parent-module "^1.0.0" - resolve-from "^4.0.0" - -is-arrayish@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - -is-core-module@^2.8.1: - version "2.9.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" - integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== - dependencies: - has "^1.0.3" - -is-json@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-json/-/is-json-2.0.1.tgz#6be166d144828a131d686891b983df62c39491ff" - integrity sha512-6BEnpVn1rcf3ngfmViLM6vjUjGErbdrL4rwlv+u1NO1XO8kqT4YGL8+19Q+Z/bas8tY90BTWMk2+fW1g6hQjbA== - -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - -jquery@^3.2.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.3.1.tgz#958ce29e81c9790f31be7792df5d4d95fc57fbca" - -"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - -jsesc@^2.5.1: - version "2.5.2" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" - integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== - -jsesc@~0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" - -json-parse-even-better-errors@^2.3.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" - integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== - -json5@^2.2.0, json5@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" - integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== - -lines-and-columns@^1.1.6: - version "1.2.4" - resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" - integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== - -lmdb-darwin-arm64@2.3.10: - version "2.3.10" - resolved "https://registry.yarnpkg.com/lmdb-darwin-arm64/-/lmdb-darwin-arm64-2.3.10.tgz#4e20f75770eeedc60af3d4630975fd105a89ffe8" - integrity sha512-LVXbH2MYu7/ZuQ8+P9rv+SwNyBKltxo7vHAGJS94HWyfwnCbKEYER9PImBvNBwzvgtaYk6x0RMX3oor6e6KdDQ== - -lmdb-darwin-x64@2.3.10: - version "2.3.10" - resolved "https://registry.yarnpkg.com/lmdb-darwin-x64/-/lmdb-darwin-x64-2.3.10.tgz#e53637a6735488eaa15feb7c0e9da142015b9476" - integrity sha512-gAc/1b/FZOb9yVOT+o0huA+hdW82oxLo5r22dFTLoRUFG1JMzxdTjmnW6ONVOHdqC9a5bt3vBCEY3jmXNqV26A== - -lmdb-linux-arm64@2.3.10: - version "2.3.10" - resolved "https://registry.yarnpkg.com/lmdb-linux-arm64/-/lmdb-linux-arm64-2.3.10.tgz#ac7db8bdfe0e9dbf2be1cc3362d6f2b79e2a9722" - integrity sha512-Ihr8mdICTK3jA4GXHxrXGK2oekn0mY6zuDSXQDNtyRSH19j3D2Y04A7SEI9S0EP/t5sjKSudYgZbiHDxRCsI5A== - -lmdb-linux-arm@2.3.10: - version "2.3.10" - resolved "https://registry.yarnpkg.com/lmdb-linux-arm/-/lmdb-linux-arm-2.3.10.tgz#74235418bbe7bf41e8ea5c9d52365c4ff5ca4b49" - integrity sha512-Rb8+4JjsThuEcJ7GLLwFkCFnoiwv/3hAAbELWITz70buQFF+dCZvCWWgEgmDTxwn5r+wIkdUjmFv4dqqiKQFmQ== - -lmdb-linux-x64@2.3.10: - version "2.3.10" - resolved "https://registry.yarnpkg.com/lmdb-linux-x64/-/lmdb-linux-x64-2.3.10.tgz#d790b95061d03c5c99a57b3ad5126f7723c60a2f" - integrity sha512-E3l3pDiCA9uvnLf+t3qkmBGRO01dp1EHD0x0g0iRnfpAhV7wYbayJGfG93BUt22Tj3fnq4HDo4dQ6ZWaDI1nuw== - -lmdb-win32-x64@2.3.10: - version "2.3.10" - resolved "https://registry.yarnpkg.com/lmdb-win32-x64/-/lmdb-win32-x64-2.3.10.tgz#bff73d12d94084343c569b16069d8d38626eb2d6" - integrity sha512-gspWk34tDANhjn+brdqZstJMptGiwj4qFNVg0Zey9ds+BUlif+Lgf5szrfOVzZ8gVRkk1Lgbz7i78+V7YK7SCA== - -lmdb@2.3.10: - version "2.3.10" - resolved "https://registry.yarnpkg.com/lmdb/-/lmdb-2.3.10.tgz#640fc60815846babcbe088d7f8ed0a51da857f6a" - integrity sha512-GtH+nStn9V59CfYeQ5ddx6YTfuFCmu86UJojIjJAweG+/Fm0PDknuk3ovgYDtY/foMeMdZa8/P7oSljW/d5UPw== - dependencies: - msgpackr "^1.5.4" - nan "^2.14.2" - node-addon-api "^4.3.0" - node-gyp-build-optional-packages "^4.3.2" - ordered-binary "^1.2.4" - weak-lru-cache "^1.2.2" - optionalDependencies: - lmdb-darwin-arm64 "2.3.10" - lmdb-darwin-x64 "2.3.10" - lmdb-linux-arm "2.3.10" - lmdb-linux-arm64 "2.3.10" - lmdb-linux-x64 "2.3.10" - lmdb-win32-x64 "2.3.10" - -lodash.debounce@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" - integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== - -lodash.get@^4.4.2: - version "4.4.2" - resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" - integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= - -lodash.isequal@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" - integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= - -lodash.isfunction@^3.0.9: - version "3.0.9" - resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051" - -lodash.isobject@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/lodash.isobject/-/lodash.isobject-3.0.2.tgz#3c8fb8d5b5bf4bf90ae06e14f2a530a4ed935e1d" - -lodash.tonumber@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/lodash.tonumber/-/lodash.tonumber-4.0.3.tgz#0b96b31b35672793eb7f5a63ee791f1b9e9025d9" - -lodash@^4.17.10: - version "4.17.10" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" - -loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" - dependencies: - js-tokens "^3.0.0 || ^4.0.0" - -mdn-data@2.0.14: - version "2.0.14" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" - integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== - -memoize-one@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-4.0.0.tgz#fc5e2f1427a216676a62ec652cf7398cfad123db" - -mini-create-react-context@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz#072171561bfdc922da08a60c2197a497cc2d1d5e" - integrity sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ== - dependencies: - "@babel/runtime" "^7.12.1" - tiny-warning "^1.0.3" - -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -msgpackr-extract@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/msgpackr-extract/-/msgpackr-extract-2.0.2.tgz#201a8d7ade47e99b3ba277c45736b00e195d4670" - integrity sha512-coskCeJG2KDny23zWeu+6tNy7BLnAiOGgiwzlgdm4oeSsTpqEJJPguHIuKZcCdB7tzhZbXNYSg6jZAXkZErkJA== - dependencies: - node-gyp-build-optional-packages "5.0.2" - optionalDependencies: - "@msgpackr-extract/msgpackr-extract-darwin-arm64" "2.0.2" - "@msgpackr-extract/msgpackr-extract-darwin-x64" "2.0.2" - "@msgpackr-extract/msgpackr-extract-linux-arm" "2.0.2" - "@msgpackr-extract/msgpackr-extract-linux-arm64" "2.0.2" - "@msgpackr-extract/msgpackr-extract-linux-x64" "2.0.2" - "@msgpackr-extract/msgpackr-extract-win32-x64" "2.0.2" - -msgpackr@^1.5.4: - version "1.6.1" - resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.6.1.tgz#4f3c94d6a5b819b838ffc736eddaf60eba436d20" - integrity sha512-Je+xBEfdjtvA4bKaOv8iRhjC8qX2oJwpYH4f7JrG4uMVJVmnmkAT4pjKdbztKprGj3iwjcxPzb5umVZ02Qq3tA== - optionalDependencies: - msgpackr-extract "^2.0.2" - -nan@^2.14.2: - version "2.16.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.16.0.tgz#664f43e45460fb98faf00edca0bb0d7b8dce7916" - integrity sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA== - -node-addon-api@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" - integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== - -node-addon-api@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f" - integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ== - -node-gyp-build-optional-packages@5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.2.tgz#3de7d30bd1f9057b5dfbaeab4a4442b7fe9c5901" - integrity sha512-PiN4NWmlQPqvbEFcH/omQsswWQbe5Z9YK/zdB23irp5j2XibaA2IrGvpSWmVVG4qMZdmPdwPctSy4a86rOMn6g== - -node-gyp-build-optional-packages@^4.3.2: - version "4.3.5" - resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-4.3.5.tgz#a1de0039f81ecacecefcbb4349cdb96842343b31" - integrity sha512-5ke7D8SiQsTQL7CkHpfR1tLwfqtKc0KYEmlnkwd40jHCASskZeS98qoZ1qDUns2aUQWikcjidRUs6PM/3iyN/w== - -node-gyp-build@^4.3.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.4.0.tgz#42e99687ce87ddeaf3a10b99dc06abc11021f3f4" - integrity sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ== - -node-releases@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.5.tgz#280ed5bc3eba0d96ce44897d8aee478bfb3d9666" - integrity sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q== - -nth-check@^2.0.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" - integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== - dependencies: - boolbase "^1.0.0" - -nullthrows@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1" - integrity sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw== - -object-assign@^4.1.0, object-assign@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - -object-keys@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - -object.assign@^4.1.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" - integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== - dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - has-symbols "^1.0.1" - object-keys "^1.1.1" - -ordered-binary@^1.2.4: - version "1.2.5" - resolved "https://registry.yarnpkg.com/ordered-binary/-/ordered-binary-1.2.5.tgz#6208c45067eae9d14b8f44791a1d7037adad9147" - integrity sha512-djRmZoEpOGvIRW7ufsCDHtvcUa18UC9TxnPbHhSVFZHsoyg0dtut1bWtBZ/fmxdPN62oWXrV6adM7NoWU+CneA== - -parcel@^2.0.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/parcel/-/parcel-2.6.0.tgz#801bd3af8339966fc52370033e1b7f3c9b9e457d" - integrity sha512-pSTJ7wC6uTl16PKLXQV7RfL9FGoIDA1iVpNvaav47n6UkUdKqfx0spcVPpw35kWdRcHJF61YAvkPjP2hTwHQ+Q== - dependencies: - "@parcel/config-default" "2.6.0" - "@parcel/core" "2.6.0" - "@parcel/diagnostic" "2.6.0" - "@parcel/events" "2.6.0" - "@parcel/fs" "2.6.0" - "@parcel/logger" "2.6.0" - "@parcel/package-manager" "2.6.0" - "@parcel/reporter-cli" "2.6.0" - "@parcel/reporter-dev-server" "2.6.0" - "@parcel/utils" "2.6.0" - chalk "^4.1.0" - commander "^7.0.0" - get-port "^4.2.0" - v8-compile-cache "^2.0.0" - -parent-module@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" - integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== - dependencies: - callsites "^3.0.0" - -parse-json@^5.0.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" - integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== - dependencies: - "@babel/code-frame" "^7.0.0" - error-ex "^1.3.1" - json-parse-even-better-errors "^2.3.0" - lines-and-columns "^1.1.6" - -path-parse@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" - integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== - -path-to-regexp@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d" - dependencies: - isarray "0.0.1" - -path-type@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" - integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== - -picocolors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" - integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== - -popper.js@^1.14.1, popper.js@^1.14.3: - version "1.14.4" - resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.14.4.tgz#8eec1d8ff02a5a3a152dd43414a15c7b79fd69b6" - -postcss-value-parser@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" - integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== - -posthtml-parser@^0.10.1: - version "0.10.2" - resolved "https://registry.yarnpkg.com/posthtml-parser/-/posthtml-parser-0.10.2.tgz#df364d7b179f2a6bf0466b56be7b98fd4e97c573" - integrity sha512-PId6zZ/2lyJi9LiKfe+i2xv57oEjJgWbsHGGANwos5AvdQp98i6AtamAl8gzSVFGfQ43Glb5D614cvZf012VKg== - dependencies: - htmlparser2 "^7.1.1" - -posthtml-parser@^0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/posthtml-parser/-/posthtml-parser-0.11.0.tgz#25d1c7bf811ea83559bc4c21c189a29747a24b7a" - integrity sha512-QecJtfLekJbWVo/dMAA+OSwY79wpRmbqS5TeXvXSX+f0c6pW4/SE6inzZ2qkU7oAMCPqIDkZDvd/bQsSFUnKyw== - dependencies: - htmlparser2 "^7.1.1" - -posthtml-render@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/posthtml-render/-/posthtml-render-3.0.0.tgz#97be44931496f495b4f07b99e903cc70ad6a3205" - integrity sha512-z+16RoxK3fUPgwaIgH9NGnK1HKY9XIDpydky5eQGgAFVXTCSezalv9U2jQuNV+Z9qV1fDWNzldcw4eK0SSbqKA== - dependencies: - is-json "^2.0.1" - -posthtml@^0.16.4, posthtml@^0.16.5: - version "0.16.6" - resolved "https://registry.yarnpkg.com/posthtml/-/posthtml-0.16.6.tgz#e2fc407f67a64d2fa3567afe770409ffdadafe59" - integrity sha512-JcEmHlyLK/o0uGAlj65vgg+7LIms0xKXe60lcDOTU7oVX/3LuEuLwrQpW3VJ7de5TaFKiW4kWkaIpJL42FEgxQ== - dependencies: - posthtml-parser "^0.11.0" - posthtml-render "^3.0.0" - -process@^0.11.10: - version "0.11.10" - resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" - integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== - -prop-types@^15.0.0, prop-types@^15.5.8, prop-types@^15.6.1, prop-types@^15.6.2: - version "15.6.2" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102" - dependencies: - loose-envify "^1.3.1" - object-assign "^4.1.1" - -prop-types@^15.7.2: - version "15.7.2" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" - integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== - dependencies: - loose-envify "^1.4.0" - object-assign "^4.1.1" - react-is "^16.8.1" - -react-ace@^9.4.1: - version "9.4.1" - resolved "https://registry.yarnpkg.com/react-ace/-/react-ace-9.4.1.tgz#5f446c2764a0b615e8d00cd251b9b35d95365e37" - integrity sha512-vhOFrB5Xy++mcPNc6wc1mwMFP/FZOnYxQPqgRl/dLfkZBbrJf4SAgXaa6PU4AXWu1u5bfxOmRwwHaZPrLb6d9Q== - dependencies: - ace-builds "^1.4.12" - diff-match-patch "^1.0.4" - lodash.get "^4.4.2" - lodash.isequal "^4.5.0" - prop-types "^15.7.2" - -react-contextmenu@^2.11.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/react-contextmenu/-/react-contextmenu-2.11.0.tgz#ec57614e6d687ceaec5c0ba97d56a302c9551d17" - integrity sha512-vT9QV9p/9h1BSIvmajRVG3KsgjuBnISpEQp0F1QYsUPFMe3VOKV2l7IiD8yrNUyXYZKrWMqI0YKsaBwGSRVgJg== - dependencies: - classnames "^2.2.5" - object-assign "^4.1.0" - -react-dom@^16.5.0: - version "16.14.0" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.14.0.tgz#7ad838ec29a777fb3c75c3a190f661cf92ab8b89" - integrity sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw== - dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - prop-types "^15.6.2" - scheduler "^0.19.1" - -react-error-overlay@6.0.9: - version "6.0.9" - resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a" - integrity sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew== - -react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1: - version "16.13.1" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" - integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== - -react-lifecycles-compat@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" - -react-popper@^0.10.4: - version "0.10.4" - resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-0.10.4.tgz#af2a415ea22291edd504678d7afda8a6ee3295aa" - dependencies: - popper.js "^1.14.1" - prop-types "^15.6.1" - -react-refresh@^0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.9.0.tgz#71863337adc3e5c2f8a6bfddd12ae3bfe32aafbf" - integrity sha512-Gvzk7OZpiqKSkxsQvO/mbTN1poglhmAV7gR/DdIrRrSMXraRQQlfikRJOr3Nb9GTMPC5kof948Zy6jJZIFtDvQ== - -react-router-dom@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.2.0.tgz#9e65a4d0c45e13289e66c7b17c7e175d0ea15662" - integrity sha512-gxAmfylo2QUjcwxI63RhQ5G85Qqt4voZpUXSEqCwykV0baaOTQDR1f0PmY8AELqIyVc0NEZUj0Gov5lNGcXgsA== - dependencies: - "@babel/runtime" "^7.1.2" - history "^4.9.0" - loose-envify "^1.3.1" - prop-types "^15.6.2" - react-router "5.2.0" - tiny-invariant "^1.0.2" - tiny-warning "^1.0.0" - -react-router@5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.2.0.tgz#424e75641ca8747fbf76e5ecca69781aa37ea293" - integrity sha512-smz1DUuFHRKdcJC0jobGo8cVbhO3x50tCL4icacOlcwDOEQPq4TMqwx3sY1TP+DvtTgz4nm3thuo7A+BK2U0Dw== - dependencies: - "@babel/runtime" "^7.1.2" - history "^4.9.0" - hoist-non-react-statics "^3.1.0" - loose-envify "^1.3.1" - mini-create-react-context "^0.4.0" - path-to-regexp "^1.7.0" - prop-types "^15.6.2" - react-is "^16.6.0" - tiny-invariant "^1.0.2" - tiny-warning "^1.0.0" - -react-table@^6.8.6: - version "6.8.6" - resolved "https://registry.yarnpkg.com/react-table/-/react-table-6.8.6.tgz#a0ad8b4839319052d5befc012603fb161e52ede3" - dependencies: - classnames "^2.2.5" - -react-transition-group@^2.3.1: - version "2.4.0" - resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.4.0.tgz#1d9391fabfd82e016f26fabd1eec329dbd922b5a" - dependencies: - dom-helpers "^3.3.1" - loose-envify "^1.3.1" - prop-types "^15.6.2" - react-lifecycles-compat "^3.0.4" - -"react@>= 16.5": - version "17.0.2" - resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" - integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== - dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - -reactdom@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/reactdom/-/reactdom-2.0.0.tgz#beeac4d698e3bf3ff0523c79d336a5ff2d532036" - -reactstrap@^6.2.0: - version "6.4.0" - resolved "https://registry.yarnpkg.com/reactstrap/-/reactstrap-6.4.0.tgz#891252a284710180cd3d746dd236ee61d28a1c66" - dependencies: - classnames "^2.2.3" - lodash.isfunction "^3.0.9" - lodash.isobject "^3.0.2" - lodash.tonumber "^4.0.3" - prop-types "^15.5.8" - react-lifecycles-compat "^3.0.4" - react-popper "^0.10.4" - react-transition-group "^2.3.1" - -regenerate-unicode-properties@^10.0.1: - version "10.0.1" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz#7f442732aa7934a3740c779bb9b3340dccc1fb56" - integrity sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw== - dependencies: - regenerate "^1.4.2" - -regenerate@^1.4.2: - version "1.4.2" - resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" - integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== - -regenerator-runtime@^0.13.4, regenerator-runtime@^0.13.7: - version "0.13.9" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" - integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== - -regenerator-transform@^0.15.0: - version "0.15.0" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.0.tgz#cbd9ead5d77fae1a48d957cf889ad0586adb6537" - integrity sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg== - dependencies: - "@babel/runtime" "^7.8.4" - -regexpu-core@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.0.1.tgz#c531122a7840de743dcf9c83e923b5560323ced3" - integrity sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw== - dependencies: - regenerate "^1.4.2" - regenerate-unicode-properties "^10.0.1" - regjsgen "^0.6.0" - regjsparser "^0.8.2" - unicode-match-property-ecmascript "^2.0.0" - unicode-match-property-value-ecmascript "^2.0.0" - -regjsgen@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.6.0.tgz#83414c5354afd7d6627b16af5f10f41c4e71808d" - integrity sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA== - -regjsparser@^0.8.2: - version "0.8.4" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.8.4.tgz#8a14285ffcc5de78c5b95d62bbf413b6bc132d5f" - integrity sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA== - dependencies: - jsesc "~0.5.0" - -resolve-from@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" - integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== - -resolve-pathname@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd" - integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng== - -resolve@^1.14.2: - version "1.22.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" - integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== - dependencies: - is-core-module "^2.8.1" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - -safe-buffer@^5.0.1, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - -scheduler@^0.19.1: - version "0.19.1" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196" - integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA== - dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - -semver@7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" - integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== - -semver@^5.7.0, semver@^5.7.1: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - -semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -source-map-support@~0.5.20: - version "0.5.21" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" - integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map@^0.6.0, source-map@^0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - -stable@^0.1.8: - version "0.1.8" - resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" - integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== - -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - dependencies: - has-flag "^3.0.0" - -supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - -supports-preserve-symlinks-flag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" - integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== - -svgo@^2.4.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24" - integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg== - dependencies: - "@trysound/sax" "0.2.0" - commander "^7.2.0" - css-select "^4.1.3" - css-tree "^1.1.3" - csso "^4.2.0" - picocolors "^1.0.0" - stable "^0.1.8" - -term-size@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.1.tgz#2a6a54840432c2fb6320fea0f415531e90189f54" - integrity sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg== - -terser@^5.2.0: - version "5.14.1" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.14.1.tgz#7c95eec36436cb11cf1902cc79ac564741d19eca" - integrity sha512-+ahUAE+iheqBTDxXhTisdA8hgvbEG1hHOQ9xmNjeUJSoi6DU/gMrKNcfZjHkyY6Alnuyc+ikYJaxxfHkT3+WuQ== - dependencies: - "@jridgewell/source-map" "^0.3.2" - acorn "^8.5.0" - commander "^2.20.0" - source-map-support "~0.5.20" - -timsort@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" - -tiny-invariant@^1.0.2: - version "1.1.0" - resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875" - integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw== - -tiny-warning@^1.0.0, tiny-warning@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" - integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== - -to-fast-properties@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" - integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== - -tslib@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" - integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== - -type-fest@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" - integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== - -unicode-canonical-property-names-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" - integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== - -unicode-match-property-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" - integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== - dependencies: - unicode-canonical-property-names-ecmascript "^2.0.0" - unicode-property-aliases-ecmascript "^2.0.0" - -unicode-match-property-value-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz#1a01aa57247c14c568b89775a54938788189a714" - integrity sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw== - -unicode-property-aliases-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz#0a36cb9a585c4f6abd51ad1deddb285c165297c8" - integrity sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ== - -utility-types@^3.10.0: - version "3.10.0" - resolved "https://registry.yarnpkg.com/utility-types/-/utility-types-3.10.0.tgz#ea4148f9a741015f05ed74fd615e1d20e6bed82b" - integrity sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg== - -v8-compile-cache@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.2.tgz#a428b28bb26790734c4fc8bc9fa106fccebf6a6c" - -value-equal@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c" - integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw== - -weak-lru-cache@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz#fdbb6741f36bae9540d12f480ce8254060dccd19" - integrity sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw== - -xxhash-wasm@^0.4.2: - version "0.4.2" - resolved "https://registry.yarnpkg.com/xxhash-wasm/-/xxhash-wasm-0.4.2.tgz#752398c131a4dd407b5132ba62ad372029be6f79" - integrity sha512-/eyHVRJQCirEkSZ1agRSCwriMhwlyUcFkXD5TPVSLP+IPzjsqMVzZwdoczLp1SoQU0R3dxz1RpIK+4YNQbCVOA== - -yaml@^1.10.0: - version "1.10.2" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" - integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== +# This file is generated by running "yarn install" inside your project. +# Manual changes might be lost - proceed with caution! + +__metadata: + version: 6 + cacheKey: 8 + +"@ampproject/remapping@npm:^2.2.0": + version: 2.2.0 + resolution: "@ampproject/remapping@npm:2.2.0" + dependencies: + "@jridgewell/gen-mapping": ^0.1.0 + "@jridgewell/trace-mapping": ^0.3.9 + checksum: d74d170d06468913921d72430259424b7e4c826b5a7d39ff839a29d547efb97dc577caa8ba3fb5cf023624e9af9d09651afc3d4112a45e2050328abc9b3a2292 + languageName: node + linkType: hard + +"@babel/code-frame@npm:^7.0.0": + version: 7.16.7 + resolution: "@babel/code-frame@npm:7.16.7" + dependencies: + "@babel/highlight": ^7.16.7 + checksum: db2f7faa31bc2c9cf63197b481b30ea57147a5fc1a6fab60e5d6c02cdfbf6de8e17b5121f99917b3dabb5eeb572da078312e70697415940383efc140d4e0808b + languageName: node + linkType: hard + +"@babel/code-frame@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/code-frame@npm:7.18.6" + dependencies: + "@babel/highlight": ^7.18.6 + checksum: 195e2be3172d7684bf95cff69ae3b7a15a9841ea9d27d3c843662d50cdd7d6470fd9c8e64be84d031117e4a4083486effba39f9aef6bbb2c89f7f21bcfba33ba + languageName: node + linkType: hard + +"@babel/compat-data@npm:^7.17.7, @babel/compat-data@npm:^7.20.1, @babel/compat-data@npm:^7.20.5": + version: 7.21.0 + resolution: "@babel/compat-data@npm:7.21.0" + checksum: dbf632c532f9c75ba0be7d1dc9f6cd3582501af52f10a6b90415d634ec5878735bd46064c91673b10317af94d4cc99c4da5bd9d955978cdccb7905fc33291e4d + languageName: node + linkType: hard + +"@babel/core@npm:^7.21.3": + version: 7.21.3 + resolution: "@babel/core@npm:7.21.3" + dependencies: + "@ampproject/remapping": ^2.2.0 + "@babel/code-frame": ^7.18.6 + "@babel/generator": ^7.21.3 + "@babel/helper-compilation-targets": ^7.20.7 + "@babel/helper-module-transforms": ^7.21.2 + "@babel/helpers": ^7.21.0 + "@babel/parser": ^7.21.3 + "@babel/template": ^7.20.7 + "@babel/traverse": ^7.21.3 + "@babel/types": ^7.21.3 + convert-source-map: ^1.7.0 + debug: ^4.1.0 + gensync: ^1.0.0-beta.2 + json5: ^2.2.2 + semver: ^6.3.0 + checksum: bef25fbea96f461bf79bd1d0e4f0cdce679fd5ada464a89c1141ddba59ae1adfdbb23e04440c266ed525712d33d5ffd818cd8b0c25b1dee0e648d5559516153a + languageName: node + linkType: hard + +"@babel/generator@npm:^7.21.3": + version: 7.21.3 + resolution: "@babel/generator@npm:7.21.3" + dependencies: + "@babel/types": ^7.21.3 + "@jridgewell/gen-mapping": ^0.3.2 + "@jridgewell/trace-mapping": ^0.3.17 + jsesc: ^2.5.1 + checksum: be6bb5a32a0273260b91210d4137b7b5da148a2db8dd324654275cb0af865ae59de5e1536e93ac83423b2586415059e1c24cf94293026755cf995757238da749 + languageName: node + linkType: hard + +"@babel/helper-annotate-as-pure@npm:^7.16.7": + version: 7.16.7 + resolution: "@babel/helper-annotate-as-pure@npm:7.16.7" + dependencies: + "@babel/types": ^7.16.7 + checksum: d235be963fed5d48a8a4cfabc41c3f03fad6a947810dbcab9cebed7f819811457e10d99b4b2e942ad71baa7ee8e3cd3f5f38a4e4685639ddfddb7528d9a07179 + languageName: node + linkType: hard + +"@babel/helper-annotate-as-pure@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/helper-annotate-as-pure@npm:7.18.6" + dependencies: + "@babel/types": ^7.18.6 + checksum: 88ccd15ced475ef2243fdd3b2916a29ea54c5db3cd0cfabf9d1d29ff6e63b7f7cd1c27264137d7a40ac2e978b9b9a542c332e78f40eb72abe737a7400788fc1b + languageName: node + linkType: hard + +"@babel/helper-builder-binary-assignment-operator-visitor@npm:^7.18.6": + version: 7.18.9 + resolution: "@babel/helper-builder-binary-assignment-operator-visitor@npm:7.18.9" + dependencies: + "@babel/helper-explode-assignable-expression": ^7.18.6 + "@babel/types": ^7.18.9 + checksum: b4bc214cb56329daff6cc18a7f7a26aeafb55a1242e5362f3d47fe3808421f8c7cd91fff95d6b9b7ccb67e14e5a67d944e49dbe026942bfcbfda19b1c72a8e72 + languageName: node + linkType: hard + +"@babel/helper-compilation-targets@npm:^7.17.7, @babel/helper-compilation-targets@npm:^7.18.9, @babel/helper-compilation-targets@npm:^7.20.0, @babel/helper-compilation-targets@npm:^7.20.7": + version: 7.20.7 + resolution: "@babel/helper-compilation-targets@npm:7.20.7" + dependencies: + "@babel/compat-data": ^7.20.5 + "@babel/helper-validator-option": ^7.18.6 + browserslist: ^4.21.3 + lru-cache: ^5.1.1 + semver: ^6.3.0 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 8c32c873ba86e2e1805b30e0807abd07188acbe00ebb97576f0b09061cc65007f1312b589eccb4349c5a8c7f8bb9f2ab199d41da7030bf103d9f347dcd3a3cf4 + languageName: node + linkType: hard + +"@babel/helper-create-class-features-plugin@npm:^7.18.6, @babel/helper-create-class-features-plugin@npm:^7.21.0": + version: 7.21.0 + resolution: "@babel/helper-create-class-features-plugin@npm:7.21.0" + dependencies: + "@babel/helper-annotate-as-pure": ^7.18.6 + "@babel/helper-environment-visitor": ^7.18.9 + "@babel/helper-function-name": ^7.21.0 + "@babel/helper-member-expression-to-functions": ^7.21.0 + "@babel/helper-optimise-call-expression": ^7.18.6 + "@babel/helper-replace-supers": ^7.20.7 + "@babel/helper-skip-transparent-expression-wrappers": ^7.20.0 + "@babel/helper-split-export-declaration": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 3e781d91d1056ea9b3a0395f3017492594a8b86899119b4a1645227c31727b8bec9bc8f6b72e86b1c5cf2dd6690893d2e8c5baff4974c429e616ead089552a21 + languageName: node + linkType: hard + +"@babel/helper-create-regexp-features-plugin@npm:^7.16.7, @babel/helper-create-regexp-features-plugin@npm:^7.17.12": + version: 7.17.12 + resolution: "@babel/helper-create-regexp-features-plugin@npm:7.17.12" + dependencies: + "@babel/helper-annotate-as-pure": ^7.16.7 + regexpu-core: ^5.0.1 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: fe49d26b0f6c58d4c1748a4d0e98b343882b428e6db43c4ba5e0aa7ff2296b3a557f0a88de9f000599bb95640a6c47c0b0c9a952b58c11f61aabb06bcc304329 + languageName: node + linkType: hard + +"@babel/helper-create-regexp-features-plugin@npm:^7.18.6, @babel/helper-create-regexp-features-plugin@npm:^7.20.5": + version: 7.21.0 + resolution: "@babel/helper-create-regexp-features-plugin@npm:7.21.0" + dependencies: + "@babel/helper-annotate-as-pure": ^7.18.6 + regexpu-core: ^5.3.1 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 63a6396a4e9444edc7e97617845583ea5cf059573d0b4cc566869f38576d543e37fde0edfcc21d6dfb7962ed241e909561714dc41c5213198bac04e0983b04f2 + languageName: node + linkType: hard + +"@babel/helper-define-polyfill-provider@npm:^0.3.3": + version: 0.3.3 + resolution: "@babel/helper-define-polyfill-provider@npm:0.3.3" + dependencies: + "@babel/helper-compilation-targets": ^7.17.7 + "@babel/helper-plugin-utils": ^7.16.7 + debug: ^4.1.1 + lodash.debounce: ^4.0.8 + resolve: ^1.14.2 + semver: ^6.1.2 + peerDependencies: + "@babel/core": ^7.4.0-0 + checksum: 8e3fe75513302e34f6d92bd67b53890e8545e6c5bca8fe757b9979f09d68d7e259f6daea90dc9e01e332c4f8781bda31c5fe551c82a277f9bc0bec007aed497c + languageName: node + linkType: hard + +"@babel/helper-environment-visitor@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/helper-environment-visitor@npm:7.18.9" + checksum: b25101f6162ddca2d12da73942c08ad203d7668e06663df685634a8fde54a98bc015f6f62938e8554457a592a024108d45b8f3e651fd6dcdb877275b73cc4420 + languageName: node + linkType: hard + +"@babel/helper-explode-assignable-expression@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/helper-explode-assignable-expression@npm:7.18.6" + dependencies: + "@babel/types": ^7.18.6 + checksum: 225cfcc3376a8799023d15dc95000609e9d4e7547b29528c7f7111a0e05493ffb12c15d70d379a0bb32d42752f340233c4115bded6d299bc0c3ab7a12be3d30f + languageName: node + linkType: hard + +"@babel/helper-function-name@npm:^7.18.9, @babel/helper-function-name@npm:^7.19.0, @babel/helper-function-name@npm:^7.21.0": + version: 7.21.0 + resolution: "@babel/helper-function-name@npm:7.21.0" + dependencies: + "@babel/template": ^7.20.7 + "@babel/types": ^7.21.0 + checksum: d63e63c3e0e3e8b3138fa47b0cd321148a300ef12b8ee951196994dcd2a492cc708aeda94c2c53759a5c9177fffaac0fd8778791286746f72a000976968daf4e + languageName: node + linkType: hard + +"@babel/helper-hoist-variables@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/helper-hoist-variables@npm:7.18.6" + dependencies: + "@babel/types": ^7.18.6 + checksum: fd9c35bb435fda802bf9ff7b6f2df06308a21277c6dec2120a35b09f9de68f68a33972e2c15505c1a1a04b36ec64c9ace97d4a9e26d6097b76b4396b7c5fa20f + languageName: node + linkType: hard + +"@babel/helper-member-expression-to-functions@npm:^7.20.7, @babel/helper-member-expression-to-functions@npm:^7.21.0": + version: 7.21.0 + resolution: "@babel/helper-member-expression-to-functions@npm:7.21.0" + dependencies: + "@babel/types": ^7.21.0 + checksum: 49cbb865098195fe82ba22da3a8fe630cde30dcd8ebf8ad5f9a24a2b685150c6711419879cf9d99b94dad24cff9244d8c2a890d3d7ec75502cd01fe58cff5b5d + languageName: node + linkType: hard + +"@babel/helper-module-imports@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/helper-module-imports@npm:7.18.6" + dependencies: + "@babel/types": ^7.18.6 + checksum: f393f8a3b3304b1b7a288a38c10989de754f01d29caf62ce7c4e5835daf0a27b81f3ac687d9d2780d39685aae7b55267324b512150e7b2be967b0c493b6a1def + languageName: node + linkType: hard + +"@babel/helper-module-transforms@npm:^7.18.6, @babel/helper-module-transforms@npm:^7.20.11, @babel/helper-module-transforms@npm:^7.21.2": + version: 7.21.2 + resolution: "@babel/helper-module-transforms@npm:7.21.2" + dependencies: + "@babel/helper-environment-visitor": ^7.18.9 + "@babel/helper-module-imports": ^7.18.6 + "@babel/helper-simple-access": ^7.20.2 + "@babel/helper-split-export-declaration": ^7.18.6 + "@babel/helper-validator-identifier": ^7.19.1 + "@babel/template": ^7.20.7 + "@babel/traverse": ^7.21.2 + "@babel/types": ^7.21.2 + checksum: 8a1c129a4f90bdf97d8b6e7861732c9580f48f877aaaafbc376ce2482febebcb8daaa1de8bc91676d12886487603f8c62a44f9e90ee76d6cac7f9225b26a49e1 + languageName: node + linkType: hard + +"@babel/helper-optimise-call-expression@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/helper-optimise-call-expression@npm:7.18.6" + dependencies: + "@babel/types": ^7.18.6 + checksum: e518fe8418571405e21644cfb39cf694f30b6c47b10b006609a92469ae8b8775cbff56f0b19732343e2ea910641091c5a2dc73b56ceba04e116a33b0f8bd2fbd + languageName: node + linkType: hard + +"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.16.7, @babel/helper-plugin-utils@npm:^7.17.12, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": + version: 7.17.12 + resolution: "@babel/helper-plugin-utils@npm:7.17.12" + checksum: 4813cf0ddb0f143de032cb88d4207024a2334951db330f8216d6fa253ea320c02c9b2667429ef1a34b5e95d4cfbd085f6cb72d418999751c31d0baf2422cc61d + languageName: node + linkType: hard + +"@babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.18.9, @babel/helper-plugin-utils@npm:^7.19.0, @babel/helper-plugin-utils@npm:^7.20.2": + version: 7.20.2 + resolution: "@babel/helper-plugin-utils@npm:7.20.2" + checksum: f6cae53b7fdb1bf3abd50fa61b10b4470985b400cc794d92635da1e7077bb19729f626adc0741b69403d9b6e411cddddb9c0157a709cc7c4eeb41e663be5d74b + languageName: node + linkType: hard + +"@babel/helper-remap-async-to-generator@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/helper-remap-async-to-generator@npm:7.18.9" + dependencies: + "@babel/helper-annotate-as-pure": ^7.18.6 + "@babel/helper-environment-visitor": ^7.18.9 + "@babel/helper-wrap-function": ^7.18.9 + "@babel/types": ^7.18.9 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 4be6076192308671b046245899b703ba090dbe7ad03e0bea897bb2944ae5b88e5e85853c9d1f83f643474b54c578d8ac0800b80341a86e8538264a725fbbefec + languageName: node + linkType: hard + +"@babel/helper-replace-supers@npm:^7.18.6, @babel/helper-replace-supers@npm:^7.20.7": + version: 7.20.7 + resolution: "@babel/helper-replace-supers@npm:7.20.7" + dependencies: + "@babel/helper-environment-visitor": ^7.18.9 + "@babel/helper-member-expression-to-functions": ^7.20.7 + "@babel/helper-optimise-call-expression": ^7.18.6 + "@babel/template": ^7.20.7 + "@babel/traverse": ^7.20.7 + "@babel/types": ^7.20.7 + checksum: b8e0087c9b0c1446e3c6f3f72b73b7e03559c6b570e2cfbe62c738676d9ebd8c369a708cf1a564ef88113b4330750a50232ee1131d303d478b7a5e65e46fbc7c + languageName: node + linkType: hard + +"@babel/helper-simple-access@npm:^7.20.2": + version: 7.20.2 + resolution: "@babel/helper-simple-access@npm:7.20.2" + dependencies: + "@babel/types": ^7.20.2 + checksum: ad1e96ee2e5f654ffee2369a586e5e8d2722bf2d8b028a121b4c33ebae47253f64d420157b9f0a8927aea3a9e0f18c0103e74fdd531815cf3650a0a4adca11a1 + languageName: node + linkType: hard + +"@babel/helper-skip-transparent-expression-wrappers@npm:^7.20.0": + version: 7.20.0 + resolution: "@babel/helper-skip-transparent-expression-wrappers@npm:7.20.0" + dependencies: + "@babel/types": ^7.20.0 + checksum: 34da8c832d1c8a546e45d5c1d59755459ffe43629436707079989599b91e8c19e50e73af7a4bd09c95402d389266731b0d9c5f69e372d8ebd3a709c05c80d7dd + languageName: node + linkType: hard + +"@babel/helper-split-export-declaration@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/helper-split-export-declaration@npm:7.18.6" + dependencies: + "@babel/types": ^7.18.6 + checksum: c6d3dede53878f6be1d869e03e9ffbbb36f4897c7cc1527dc96c56d127d834ffe4520a6f7e467f5b6f3c2843ea0e81a7819d66ae02f707f6ac057f3d57943a2b + languageName: node + linkType: hard + +"@babel/helper-string-parser@npm:^7.19.4": + version: 7.19.4 + resolution: "@babel/helper-string-parser@npm:7.19.4" + checksum: b2f8a3920b30dfac81ec282ac4ad9598ea170648f8254b10f475abe6d944808fb006aab325d3eb5a8ad3bea8dfa888cfa6ef471050dae5748497c110ec060943 + languageName: node + linkType: hard + +"@babel/helper-validator-identifier@npm:^7.16.7": + version: 7.16.7 + resolution: "@babel/helper-validator-identifier@npm:7.16.7" + checksum: dbb3db9d184343152520a209b5684f5e0ed416109cde82b428ca9c759c29b10c7450657785a8b5c5256aa74acc6da491c1f0cf6b784939f7931ef82982051b69 + languageName: node + linkType: hard + +"@babel/helper-validator-identifier@npm:^7.18.6, @babel/helper-validator-identifier@npm:^7.19.1": + version: 7.19.1 + resolution: "@babel/helper-validator-identifier@npm:7.19.1" + checksum: 0eca5e86a729162af569b46c6c41a63e18b43dbe09fda1d2a3c8924f7d617116af39cac5e4cd5d431bb760b4dca3c0970e0c444789b1db42bcf1fa41fbad0a3a + languageName: node + linkType: hard + +"@babel/helper-validator-option@npm:^7.18.6": + version: 7.21.0 + resolution: "@babel/helper-validator-option@npm:7.21.0" + checksum: 8ece4c78ffa5461fd8ab6b6e57cc51afad59df08192ed5d84b475af4a7193fc1cb794b59e3e7be64f3cdc4df7ac78bf3dbb20c129d7757ae078e6279ff8c2f07 + languageName: node + linkType: hard + +"@babel/helper-wrap-function@npm:^7.18.9": + version: 7.20.5 + resolution: "@babel/helper-wrap-function@npm:7.20.5" + dependencies: + "@babel/helper-function-name": ^7.19.0 + "@babel/template": ^7.18.10 + "@babel/traverse": ^7.20.5 + "@babel/types": ^7.20.5 + checksum: 11a6fc28334368a193a9cb3ad16f29cd7603bab958433efc82ebe59fa6556c227faa24f07ce43983f7a85df826f71d441638442c4315e90a554fe0a70ca5005b + languageName: node + linkType: hard + +"@babel/helpers@npm:^7.21.0": + version: 7.21.0 + resolution: "@babel/helpers@npm:7.21.0" + dependencies: + "@babel/template": ^7.20.7 + "@babel/traverse": ^7.21.0 + "@babel/types": ^7.21.0 + checksum: 9370dad2bb665c551869a08ac87c8bdafad53dbcdce1f5c5d498f51811456a3c005d9857562715151a0f00b2e912ac8d89f56574f837b5689f5f5072221cdf54 + languageName: node + linkType: hard + +"@babel/highlight@npm:^7.16.7": + version: 7.17.12 + resolution: "@babel/highlight@npm:7.17.12" + dependencies: + "@babel/helper-validator-identifier": ^7.16.7 + chalk: ^2.0.0 + js-tokens: ^4.0.0 + checksum: 841a11aa353113bcce662b47085085a379251bf8b09054e37e1e082da1bf0d59355a556192a6b5e9ee98e8ee6f1f2831ac42510633c5e7043e3744dda2d6b9d6 + languageName: node + linkType: hard + +"@babel/highlight@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/highlight@npm:7.18.6" + dependencies: + "@babel/helper-validator-identifier": ^7.18.6 + chalk: ^2.0.0 + js-tokens: ^4.0.0 + checksum: 92d8ee61549de5ff5120e945e774728e5ccd57fd3b2ed6eace020ec744823d4a98e242be1453d21764a30a14769ecd62170fba28539b211799bbaf232bbb2789 + languageName: node + linkType: hard + +"@babel/parser@npm:^7.20.7, @babel/parser@npm:^7.21.3": + version: 7.21.3 + resolution: "@babel/parser@npm:7.21.3" + bin: + parser: ./bin/babel-parser.js + checksum: a71e6456a1260c2a943736b56cc0acdf5f2a53c6c79e545f56618967e51f9b710d1d3359264e7c979313a7153741b1d95ad8860834cc2ab4ce4f428b13cc07be + languageName: node + linkType: hard + +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.18.6" + dependencies: + "@babel/helper-plugin-utils": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 845bd280c55a6a91d232cfa54eaf9708ec71e594676fe705794f494bb8b711d833b752b59d1a5c154695225880c23dbc9cab0e53af16fd57807976cd3ff41b8d + languageName: node + linkType: hard + +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.18.9": + version: 7.20.7 + resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.20.7" + dependencies: + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/helper-skip-transparent-expression-wrappers": ^7.20.0 + "@babel/plugin-proposal-optional-chaining": ^7.20.7 + peerDependencies: + "@babel/core": ^7.13.0 + checksum: d610f532210bee5342f5b44a12395ccc6d904e675a297189bc1e401cc185beec09873da523466d7fec34ae1574f7a384235cba1ccc9fe7b89ba094167897c845 + languageName: node + linkType: hard + +"@babel/plugin-proposal-async-generator-functions@npm:^7.20.1": + version: 7.20.7 + resolution: "@babel/plugin-proposal-async-generator-functions@npm:7.20.7" + dependencies: + "@babel/helper-environment-visitor": ^7.18.9 + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/helper-remap-async-to-generator": ^7.18.9 + "@babel/plugin-syntax-async-generators": ^7.8.4 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 111109ee118c9e69982f08d5e119eab04190b36a0f40e22e873802d941956eee66d2aa5a15f5321e51e3f9aa70a91136451b987fe15185ef8cc547ac88937723 + languageName: node + linkType: hard + +"@babel/plugin-proposal-class-properties@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-proposal-class-properties@npm:7.18.6" + dependencies: + "@babel/helper-create-class-features-plugin": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 49a78a2773ec0db56e915d9797e44fd079ab8a9b2e1716e0df07c92532f2c65d76aeda9543883916b8e0ff13606afeffa67c5b93d05b607bc87653ad18a91422 + languageName: node + linkType: hard + +"@babel/plugin-proposal-class-static-block@npm:^7.18.6": + version: 7.21.0 + resolution: "@babel/plugin-proposal-class-static-block@npm:7.21.0" + dependencies: + "@babel/helper-create-class-features-plugin": ^7.21.0 + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/plugin-syntax-class-static-block": ^7.14.5 + peerDependencies: + "@babel/core": ^7.12.0 + checksum: 236c0ad089e7a7acab776cc1d355330193314bfcd62e94e78f2df35817c6144d7e0e0368976778afd6b7c13e70b5068fa84d7abbf967d4f182e60d03f9ef802b + languageName: node + linkType: hard + +"@babel/plugin-proposal-dynamic-import@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-proposal-dynamic-import@npm:7.18.6" + dependencies: + "@babel/helper-plugin-utils": ^7.18.6 + "@babel/plugin-syntax-dynamic-import": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 96b1c8a8ad8171d39e9ab106be33bde37ae09b22fb2c449afee9a5edf3c537933d79d963dcdc2694d10677cb96da739cdf1b53454e6a5deab9801f28a818bb2f + languageName: node + linkType: hard + +"@babel/plugin-proposal-export-namespace-from@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/plugin-proposal-export-namespace-from@npm:7.18.9" + dependencies: + "@babel/helper-plugin-utils": ^7.18.9 + "@babel/plugin-syntax-export-namespace-from": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 84ff22bacc5d30918a849bfb7e0e90ae4c5b8d8b65f2ac881803d1cf9068dffbe53bd657b0e4bc4c20b4db301b1c85f1e74183cf29a0dd31e964bd4e97c363ef + languageName: node + linkType: hard + +"@babel/plugin-proposal-json-strings@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-proposal-json-strings@npm:7.18.6" + dependencies: + "@babel/helper-plugin-utils": ^7.18.6 + "@babel/plugin-syntax-json-strings": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 25ba0e6b9d6115174f51f7c6787e96214c90dd4026e266976b248a2ed417fe50fddae72843ffb3cbe324014a18632ce5648dfac77f089da858022b49fd608cb3 + languageName: node + linkType: hard + +"@babel/plugin-proposal-logical-assignment-operators@npm:^7.18.9": + version: 7.20.7 + resolution: "@babel/plugin-proposal-logical-assignment-operators@npm:7.20.7" + dependencies: + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/plugin-syntax-logical-assignment-operators": ^7.10.4 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: cdd7b8136cc4db3f47714d5266f9e7b592a2ac5a94a5878787ce08890e97c8ab1ca8e94b27bfeba7b0f2b1549a026d9fc414ca2196de603df36fb32633bbdc19 + languageName: node + linkType: hard + +"@babel/plugin-proposal-nullish-coalescing-operator@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-proposal-nullish-coalescing-operator@npm:7.18.6" + dependencies: + "@babel/helper-plugin-utils": ^7.18.6 + "@babel/plugin-syntax-nullish-coalescing-operator": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 949c9ddcdecdaec766ee610ef98f965f928ccc0361dd87cf9f88cf4896a6ccd62fce063d4494778e50da99dea63d270a1be574a62d6ab81cbe9d85884bf55a7d + languageName: node + linkType: hard + +"@babel/plugin-proposal-numeric-separator@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-proposal-numeric-separator@npm:7.18.6" + dependencies: + "@babel/helper-plugin-utils": ^7.18.6 + "@babel/plugin-syntax-numeric-separator": ^7.10.4 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: f370ea584c55bf4040e1f78c80b4eeb1ce2e6aaa74f87d1a48266493c33931d0b6222d8cee3a082383d6bb648ab8d6b7147a06f974d3296ef3bc39c7851683ec + languageName: node + linkType: hard + +"@babel/plugin-proposal-object-rest-spread@npm:^7.20.2": + version: 7.20.7 + resolution: "@babel/plugin-proposal-object-rest-spread@npm:7.20.7" + dependencies: + "@babel/compat-data": ^7.20.5 + "@babel/helper-compilation-targets": ^7.20.7 + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/plugin-syntax-object-rest-spread": ^7.8.3 + "@babel/plugin-transform-parameters": ^7.20.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 1329db17009964bc644484c660eab717cb3ca63ac0ab0f67c651a028d1bc2ead51dc4064caea283e46994f1b7221670a35cbc0b4beb6273f55e915494b5aa0b2 + languageName: node + linkType: hard + +"@babel/plugin-proposal-optional-catch-binding@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-proposal-optional-catch-binding@npm:7.18.6" + dependencies: + "@babel/helper-plugin-utils": ^7.18.6 + "@babel/plugin-syntax-optional-catch-binding": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 7b5b39fb5d8d6d14faad6cb68ece5eeb2fd550fb66b5af7d7582402f974f5bc3684641f7c192a5a57e0f59acfae4aada6786be1eba030881ddc590666eff4d1e + languageName: node + linkType: hard + +"@babel/plugin-proposal-optional-chaining@npm:^7.18.9, @babel/plugin-proposal-optional-chaining@npm:^7.20.7": + version: 7.21.0 + resolution: "@babel/plugin-proposal-optional-chaining@npm:7.21.0" + dependencies: + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/helper-skip-transparent-expression-wrappers": ^7.20.0 + "@babel/plugin-syntax-optional-chaining": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 11c5449e01b18bb8881e8e005a577fa7be2fe5688e2382c8822d51f8f7005342a301a46af7b273b1f5645f9a7b894c428eee8526342038a275ef6ba4c8d8d746 + languageName: node + linkType: hard + +"@babel/plugin-proposal-private-methods@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-proposal-private-methods@npm:7.18.6" + dependencies: + "@babel/helper-create-class-features-plugin": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 22d8502ee96bca99ad2c8393e8493e2b8d4507576dd054490fd8201a36824373440106f5b098b6d821b026c7e72b0424ff4aeca69ed5f42e48f029d3a156d5ad + languageName: node + linkType: hard + +"@babel/plugin-proposal-private-property-in-object@npm:^7.18.6": + version: 7.21.0 + resolution: "@babel/plugin-proposal-private-property-in-object@npm:7.21.0" + dependencies: + "@babel/helper-annotate-as-pure": ^7.18.6 + "@babel/helper-create-class-features-plugin": ^7.21.0 + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/plugin-syntax-private-property-in-object": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: add881a6a836635c41d2710551fdf777e2c07c0b691bf2baacc5d658dd64107479df1038680d6e67c468bfc6f36fb8920025d6bac2a1df0a81b867537d40ae78 + languageName: node + linkType: hard + +"@babel/plugin-proposal-unicode-property-regex@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-proposal-unicode-property-regex@npm:7.18.6" + dependencies: + "@babel/helper-create-regexp-features-plugin": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: a8575ecb7ff24bf6c6e94808d5c84bb5a0c6dd7892b54f09f4646711ba0ee1e1668032b3c43e3e1dfec2c5716c302e851ac756c1645e15882d73df6ad21ae951 + languageName: node + linkType: hard + +"@babel/plugin-proposal-unicode-property-regex@npm:^7.4.4": + version: 7.17.12 + resolution: "@babel/plugin-proposal-unicode-property-regex@npm:7.17.12" + dependencies: + "@babel/helper-create-regexp-features-plugin": ^7.17.12 + "@babel/helper-plugin-utils": ^7.17.12 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 0e4194510415ed11849f1617fcb32d996df746ba93cd05ebbabecb63cfc02c0e97b585c97da3dcf68acdd3c8b71cfae964abe5d5baba6bd3977a475d9225ad9e + languageName: node + linkType: hard + +"@babel/plugin-syntax-async-generators@npm:^7.8.4": + version: 7.8.4 + resolution: "@babel/plugin-syntax-async-generators@npm:7.8.4" + dependencies: + "@babel/helper-plugin-utils": ^7.8.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 7ed1c1d9b9e5b64ef028ea5e755c0be2d4e5e4e3d6cf7df757b9a8c4cfa4193d268176d0f1f7fbecdda6fe722885c7fda681f480f3741d8a2d26854736f05367 + languageName: node + linkType: hard + +"@babel/plugin-syntax-class-properties@npm:^7.12.13": + version: 7.12.13 + resolution: "@babel/plugin-syntax-class-properties@npm:7.12.13" + dependencies: + "@babel/helper-plugin-utils": ^7.12.13 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 24f34b196d6342f28d4bad303612d7ff566ab0a013ce89e775d98d6f832969462e7235f3e7eaf17678a533d4be0ba45d3ae34ab4e5a9dcbda5d98d49e5efa2fc + languageName: node + linkType: hard + +"@babel/plugin-syntax-class-static-block@npm:^7.14.5": + version: 7.14.5 + resolution: "@babel/plugin-syntax-class-static-block@npm:7.14.5" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 3e80814b5b6d4fe17826093918680a351c2d34398a914ce6e55d8083d72a9bdde4fbaf6a2dcea0e23a03de26dc2917ae3efd603d27099e2b98380345703bf948 + languageName: node + linkType: hard + +"@babel/plugin-syntax-dynamic-import@npm:^7.8.3": + version: 7.8.3 + resolution: "@babel/plugin-syntax-dynamic-import@npm:7.8.3" + dependencies: + "@babel/helper-plugin-utils": ^7.8.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: ce307af83cf433d4ec42932329fad25fa73138ab39c7436882ea28742e1c0066626d224e0ad2988724c82644e41601cef607b36194f695cb78a1fcdc959637bd + languageName: node + linkType: hard + +"@babel/plugin-syntax-export-namespace-from@npm:^7.8.3": + version: 7.8.3 + resolution: "@babel/plugin-syntax-export-namespace-from@npm:7.8.3" + dependencies: + "@babel/helper-plugin-utils": ^7.8.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 85740478be5b0de185228e7814451d74ab8ce0a26fcca7613955262a26e99e8e15e9da58f60c754b84515d4c679b590dbd3f2148f0f58025f4ae706f1c5a5d4a + languageName: node + linkType: hard + +"@babel/plugin-syntax-import-assertions@npm:^7.20.0": + version: 7.20.0 + resolution: "@babel/plugin-syntax-import-assertions@npm:7.20.0" + dependencies: + "@babel/helper-plugin-utils": ^7.19.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 6a86220e0aae40164cd3ffaf80e7c076a1be02a8f3480455dddbae05fda8140f429290027604df7a11b3f3f124866e8a6d69dbfa1dda61ee7377b920ad144d5b + languageName: node + linkType: hard + +"@babel/plugin-syntax-json-strings@npm:^7.8.3": + version: 7.8.3 + resolution: "@babel/plugin-syntax-json-strings@npm:7.8.3" + dependencies: + "@babel/helper-plugin-utils": ^7.8.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: bf5aea1f3188c9a507e16efe030efb996853ca3cadd6512c51db7233cc58f3ac89ff8c6bdfb01d30843b161cfe7d321e1bf28da82f7ab8d7e6bc5464666f354a + languageName: node + linkType: hard + +"@babel/plugin-syntax-jsx@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-syntax-jsx@npm:7.18.6" + dependencies: + "@babel/helper-plugin-utils": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 6d37ea972970195f1ffe1a54745ce2ae456e0ac6145fae9aa1480f297248b262ea6ebb93010eddb86ebfacb94f57c05a1fc5d232b9a67325b09060299d515c67 + languageName: node + linkType: hard + +"@babel/plugin-syntax-logical-assignment-operators@npm:^7.10.4": + version: 7.10.4 + resolution: "@babel/plugin-syntax-logical-assignment-operators@npm:7.10.4" + dependencies: + "@babel/helper-plugin-utils": ^7.10.4 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: aff33577037e34e515911255cdbb1fd39efee33658aa00b8a5fd3a4b903585112d037cce1cc9e4632f0487dc554486106b79ccd5ea63a2e00df4363f6d4ff886 + languageName: node + linkType: hard + +"@babel/plugin-syntax-nullish-coalescing-operator@npm:^7.8.3": + version: 7.8.3 + resolution: "@babel/plugin-syntax-nullish-coalescing-operator@npm:7.8.3" + dependencies: + "@babel/helper-plugin-utils": ^7.8.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 87aca4918916020d1fedba54c0e232de408df2644a425d153be368313fdde40d96088feed6c4e5ab72aac89be5d07fef2ddf329a15109c5eb65df006bf2580d1 + languageName: node + linkType: hard + +"@babel/plugin-syntax-numeric-separator@npm:^7.10.4": + version: 7.10.4 + resolution: "@babel/plugin-syntax-numeric-separator@npm:7.10.4" + dependencies: + "@babel/helper-plugin-utils": ^7.10.4 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 01ec5547bd0497f76cc903ff4d6b02abc8c05f301c88d2622b6d834e33a5651aa7c7a3d80d8d57656a4588f7276eba357f6b7e006482f5b564b7a6488de493a1 + languageName: node + linkType: hard + +"@babel/plugin-syntax-object-rest-spread@npm:^7.8.3": + version: 7.8.3 + resolution: "@babel/plugin-syntax-object-rest-spread@npm:7.8.3" + dependencies: + "@babel/helper-plugin-utils": ^7.8.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: fddcf581a57f77e80eb6b981b10658421bc321ba5f0a5b754118c6a92a5448f12a0c336f77b8abf734841e102e5126d69110a306eadb03ca3e1547cab31f5cbf + languageName: node + linkType: hard + +"@babel/plugin-syntax-optional-catch-binding@npm:^7.8.3": + version: 7.8.3 + resolution: "@babel/plugin-syntax-optional-catch-binding@npm:7.8.3" + dependencies: + "@babel/helper-plugin-utils": ^7.8.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 910d90e72bc90ea1ce698e89c1027fed8845212d5ab588e35ef91f13b93143845f94e2539d831dc8d8ededc14ec02f04f7bd6a8179edd43a326c784e7ed7f0b9 + languageName: node + linkType: hard + +"@babel/plugin-syntax-optional-chaining@npm:^7.8.3": + version: 7.8.3 + resolution: "@babel/plugin-syntax-optional-chaining@npm:7.8.3" + dependencies: + "@babel/helper-plugin-utils": ^7.8.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: eef94d53a1453361553c1f98b68d17782861a04a392840341bc91780838dd4e695209c783631cf0de14c635758beafb6a3a65399846ffa4386bff90639347f30 + languageName: node + linkType: hard + +"@babel/plugin-syntax-private-property-in-object@npm:^7.14.5": + version: 7.14.5 + resolution: "@babel/plugin-syntax-private-property-in-object@npm:7.14.5" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: b317174783e6e96029b743ccff2a67d63d38756876e7e5d0ba53a322e38d9ca452c13354a57de1ad476b4c066dbae699e0ca157441da611117a47af88985ecda + languageName: node + linkType: hard + +"@babel/plugin-syntax-top-level-await@npm:^7.14.5": + version: 7.14.5 + resolution: "@babel/plugin-syntax-top-level-await@npm:7.14.5" + dependencies: + "@babel/helper-plugin-utils": ^7.14.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: bbd1a56b095be7820029b209677b194db9b1d26691fe999856462e66b25b281f031f3dfd91b1619e9dcf95bebe336211833b854d0fb8780d618e35667c2d0d7e + languageName: node + linkType: hard + +"@babel/plugin-transform-arrow-functions@npm:^7.18.6": + version: 7.20.7 + resolution: "@babel/plugin-transform-arrow-functions@npm:7.20.7" + dependencies: + "@babel/helper-plugin-utils": ^7.20.2 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: b43cabe3790c2de7710abe32df9a30005eddb2050dadd5d122c6872f679e5710e410f1b90c8f99a2aff7b614cccfecf30e7fd310236686f60d3ed43fd80b9847 + languageName: node + linkType: hard + +"@babel/plugin-transform-async-to-generator@npm:^7.18.6": + version: 7.20.7 + resolution: "@babel/plugin-transform-async-to-generator@npm:7.20.7" + dependencies: + "@babel/helper-module-imports": ^7.18.6 + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/helper-remap-async-to-generator": ^7.18.9 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: fe9ee8a5471b4317c1b9ea92410ace8126b52a600d7cfbfe1920dcac6fb0fad647d2e08beb4fd03c630eb54430e6c72db11e283e3eddc49615c68abd39430904 + languageName: node + linkType: hard + +"@babel/plugin-transform-block-scoped-functions@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.18.6" + dependencies: + "@babel/helper-plugin-utils": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 0a0df61f94601e3666bf39f2cc26f5f7b22a94450fb93081edbed967bd752ce3f81d1227fefd3799f5ee2722171b5e28db61379234d1bb85b6ec689589f99d7e + languageName: node + linkType: hard + +"@babel/plugin-transform-block-scoping@npm:^7.20.2": + version: 7.21.0 + resolution: "@babel/plugin-transform-block-scoping@npm:7.21.0" + dependencies: + "@babel/helper-plugin-utils": ^7.20.2 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 15aacaadbecf96b53a750db1be4990b0d89c7f5bc3e1794b63b49fb219638c1fd25d452d15566d7e5ddf5b5f4e1a0a0055c35c1c7aee323c7b114bf49f66f4b0 + languageName: node + linkType: hard + +"@babel/plugin-transform-classes@npm:^7.20.2": + version: 7.21.0 + resolution: "@babel/plugin-transform-classes@npm:7.21.0" + dependencies: + "@babel/helper-annotate-as-pure": ^7.18.6 + "@babel/helper-compilation-targets": ^7.20.7 + "@babel/helper-environment-visitor": ^7.18.9 + "@babel/helper-function-name": ^7.21.0 + "@babel/helper-optimise-call-expression": ^7.18.6 + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/helper-replace-supers": ^7.20.7 + "@babel/helper-split-export-declaration": ^7.18.6 + globals: ^11.1.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 088ae152074bd0e90f64659169255bfe50393e637ec8765cb2a518848b11b0299e66b91003728fd0a41563a6fdc6b8d548ece698a314fd5447f5489c22e466b7 + languageName: node + linkType: hard + +"@babel/plugin-transform-computed-properties@npm:^7.18.9": + version: 7.20.7 + resolution: "@babel/plugin-transform-computed-properties@npm:7.20.7" + dependencies: + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/template": ^7.20.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: be70e54bda8b469146459f429e5f2bd415023b87b2d5af8b10e48f465ffb02847a3ed162ca60378c004b82db848e4d62e90010d41ded7e7176b6d8d1c2911139 + languageName: node + linkType: hard + +"@babel/plugin-transform-destructuring@npm:^7.20.2": + version: 7.21.3 + resolution: "@babel/plugin-transform-destructuring@npm:7.21.3" + dependencies: + "@babel/helper-plugin-utils": ^7.20.2 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 43ebbe0bfa20287e34427be7c2200ce096c20913775ea75268fb47fe0e55f9510800587e6052c42fe6dffa0daaad95dd465c3e312fd1ef9785648384c45417ac + languageName: node + linkType: hard + +"@babel/plugin-transform-dotall-regex@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-dotall-regex@npm:7.18.6" + dependencies: + "@babel/helper-create-regexp-features-plugin": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: cbe5d7063eb8f8cca24cd4827bc97f5641166509e58781a5f8aa47fb3d2d786ce4506a30fca2e01f61f18792783a5cb5d96bf5434c3dd1ad0de8c9cc625a53da + languageName: node + linkType: hard + +"@babel/plugin-transform-dotall-regex@npm:^7.4.4": + version: 7.16.7 + resolution: "@babel/plugin-transform-dotall-regex@npm:7.16.7" + dependencies: + "@babel/helper-create-regexp-features-plugin": ^7.16.7 + "@babel/helper-plugin-utils": ^7.16.7 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 554570dddfd5bfd87ab307be520f69a3d4ed2d2db677c165971b400d4c96656d0c165b318e69f1735612dcd12e04c0ee257697dc26800e8a572ca73bc05fa0f4 + languageName: node + linkType: hard + +"@babel/plugin-transform-duplicate-keys@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/plugin-transform-duplicate-keys@npm:7.18.9" + dependencies: + "@babel/helper-plugin-utils": ^7.18.9 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 220bf4a9fec5c4d4a7b1de38810350260e8ea08481bf78332a464a21256a95f0df8cd56025f346238f09b04f8e86d4158fafc9f4af57abaef31637e3b58bd4fe + languageName: node + linkType: hard + +"@babel/plugin-transform-exponentiation-operator@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.18.6" + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 7f70222f6829c82a36005508d34ddbe6fd0974ae190683a8670dd6ff08669aaf51fef2209d7403f9bd543cb2d12b18458016c99a6ed0332ccedb3ea127b01229 + languageName: node + linkType: hard + +"@babel/plugin-transform-for-of@npm:^7.18.8": + version: 7.21.0 + resolution: "@babel/plugin-transform-for-of@npm:7.21.0" + dependencies: + "@babel/helper-plugin-utils": ^7.20.2 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 2f3f86ca1fab2929fcda6a87e4303d5c635b5f96dc9a45fd4ca083308a3020c79ac33b9543eb4640ef2b79f3586a00ab2d002a7081adb9e9d7440dce30781034 + languageName: node + linkType: hard + +"@babel/plugin-transform-function-name@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/plugin-transform-function-name@npm:7.18.9" + dependencies: + "@babel/helper-compilation-targets": ^7.18.9 + "@babel/helper-function-name": ^7.18.9 + "@babel/helper-plugin-utils": ^7.18.9 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 62dd9c6cdc9714704efe15545e782ee52d74dc73916bf954b4d3bee088fb0ec9e3c8f52e751252433656c09f744b27b757fc06ed99bcde28e8a21600a1d8e597 + languageName: node + linkType: hard + +"@babel/plugin-transform-literals@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/plugin-transform-literals@npm:7.18.9" + dependencies: + "@babel/helper-plugin-utils": ^7.18.9 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 3458dd2f1a47ac51d9d607aa18f3d321cbfa8560a985199185bed5a906bb0c61ba85575d386460bac9aed43fdd98940041fae5a67dff286f6f967707cff489f8 + languageName: node + linkType: hard + +"@babel/plugin-transform-member-expression-literals@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-member-expression-literals@npm:7.18.6" + dependencies: + "@babel/helper-plugin-utils": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 35a3d04f6693bc6b298c05453d85ee6e41cc806538acb6928427e0e97ae06059f97d2f07d21495fcf5f70d3c13a242e2ecbd09d5c1fcb1b1a73ff528dcb0b695 + languageName: node + linkType: hard + +"@babel/plugin-transform-modules-amd@npm:^7.19.6": + version: 7.20.11 + resolution: "@babel/plugin-transform-modules-amd@npm:7.20.11" + dependencies: + "@babel/helper-module-transforms": ^7.20.11 + "@babel/helper-plugin-utils": ^7.20.2 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 23665c1c20c8f11c89382b588fb9651c0756d130737a7625baeaadbd3b973bc5bfba1303bedffa8fb99db1e6d848afb01016e1df2b69b18303e946890c790001 + languageName: node + linkType: hard + +"@babel/plugin-transform-modules-commonjs@npm:^7.19.6": + version: 7.21.2 + resolution: "@babel/plugin-transform-modules-commonjs@npm:7.21.2" + dependencies: + "@babel/helper-module-transforms": ^7.21.2 + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/helper-simple-access": ^7.20.2 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 65aa06e3e3792f39b99eb5f807034693ff0ecf80438580f7ae504f4c4448ef04147b1889ea5e6f60f3ad4a12ebbb57c6f1f979a249dadbd8d11fe22f4441918b + languageName: node + linkType: hard + +"@babel/plugin-transform-modules-systemjs@npm:^7.19.6": + version: 7.20.11 + resolution: "@babel/plugin-transform-modules-systemjs@npm:7.20.11" + dependencies: + "@babel/helper-hoist-variables": ^7.18.6 + "@babel/helper-module-transforms": ^7.20.11 + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/helper-validator-identifier": ^7.19.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 4546c47587f88156d66c7eb7808e903cf4bb3f6ba6ac9bc8e3af2e29e92eb9f0b3f44d52043bfd24eb25fa7827fd7b6c8bfeac0cac7584e019b87e1ecbd0e673 + languageName: node + linkType: hard + +"@babel/plugin-transform-modules-umd@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-modules-umd@npm:7.18.6" + dependencies: + "@babel/helper-module-transforms": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: c3b6796c6f4579f1ba5ab0cdcc73910c1e9c8e1e773c507c8bb4da33072b3ae5df73c6d68f9126dab6e99c24ea8571e1563f8710d7c421fac1cde1e434c20153 + languageName: node + linkType: hard + +"@babel/plugin-transform-named-capturing-groups-regex@npm:^7.19.1": + version: 7.20.5 + resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.20.5" + dependencies: + "@babel/helper-create-regexp-features-plugin": ^7.20.5 + "@babel/helper-plugin-utils": ^7.20.2 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 528c95fb1087e212f17e1c6456df041b28a83c772b9c93d2e407c9d03b72182b0d9d126770c1d6e0b23aab052599ceaf25ed6a2c0627f4249be34a83f6fae853 + languageName: node + linkType: hard + +"@babel/plugin-transform-new-target@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-new-target@npm:7.18.6" + dependencies: + "@babel/helper-plugin-utils": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: bd780e14f46af55d0ae8503b3cb81ca86dcc73ed782f177e74f498fff934754f9e9911df1f8f3bd123777eed7c1c1af4d66abab87c8daae5403e7719a6b845d1 + languageName: node + linkType: hard + +"@babel/plugin-transform-object-super@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-object-super@npm:7.18.6" + dependencies: + "@babel/helper-plugin-utils": ^7.18.6 + "@babel/helper-replace-supers": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 0fcb04e15deea96ae047c21cb403607d49f06b23b4589055993365ebd7a7d7541334f06bf9642e90075e66efce6ebaf1eb0ef066fbbab802d21d714f1aac3aef + languageName: node + linkType: hard + +"@babel/plugin-transform-parameters@npm:^7.20.1, @babel/plugin-transform-parameters@npm:^7.20.7": + version: 7.21.3 + resolution: "@babel/plugin-transform-parameters@npm:7.21.3" + dependencies: + "@babel/helper-plugin-utils": ^7.20.2 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: c92128d7b1fcf54e2cab186c196bbbf55a9a6de11a83328dc2602649c9dc6d16ef73712beecd776cd49bfdc624b5f56740f4a53568d3deb9505ec666bc869da3 + languageName: node + linkType: hard + +"@babel/plugin-transform-property-literals@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-property-literals@npm:7.18.6" + dependencies: + "@babel/helper-plugin-utils": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 1c16e64de554703f4b547541de2edda6c01346dd3031d4d29e881aa7733785cd26d53611a4ccf5353f4d3e69097bb0111c0a93ace9e683edd94fea28c4484144 + languageName: node + linkType: hard + +"@babel/plugin-transform-react-display-name@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-react-display-name@npm:7.18.6" + dependencies: + "@babel/helper-plugin-utils": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 51c087ab9e41ef71a29335587da28417536c6f816c292e092ffc0e0985d2f032656801d4dd502213ce32481f4ba6c69402993ffa67f0818a07606ff811e4be49 + languageName: node + linkType: hard + +"@babel/plugin-transform-react-jsx-development@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-react-jsx-development@npm:7.18.6" + dependencies: + "@babel/plugin-transform-react-jsx": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: ec9fa65db66f938b75c45e99584367779ac3e0af8afc589187262e1337c7c4205ea312877813ae4df9fb93d766627b8968d74ac2ba702e4883b1dbbe4953ecee + languageName: node + linkType: hard + +"@babel/plugin-transform-react-jsx@npm:^7.18.6": + version: 7.21.0 + resolution: "@babel/plugin-transform-react-jsx@npm:7.21.0" + dependencies: + "@babel/helper-annotate-as-pure": ^7.18.6 + "@babel/helper-module-imports": ^7.18.6 + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/plugin-syntax-jsx": ^7.18.6 + "@babel/types": ^7.21.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: c77d277d2e55b489a9b9be185c3eed5d8e2c87046778810f8e47ee3c87b47e64cad93c02211c968486c7958fd05ce203c66779446484c98a7b3a69bec687d5dc + languageName: node + linkType: hard + +"@babel/plugin-transform-react-pure-annotations@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-react-pure-annotations@npm:7.18.6" + dependencies: + "@babel/helper-annotate-as-pure": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 97c4873d409088f437f9084d084615948198dd87fc6723ada0e7e29c5a03623c2f3e03df3f52e7e7d4d23be32a08ea00818bff302812e48713c706713bd06219 + languageName: node + linkType: hard + +"@babel/plugin-transform-regenerator@npm:^7.18.6": + version: 7.20.5 + resolution: "@babel/plugin-transform-regenerator@npm:7.20.5" + dependencies: + "@babel/helper-plugin-utils": ^7.20.2 + regenerator-transform: ^0.15.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 13164861e71fb23d84c6270ef5330b03c54d5d661c2c7468f28e21c4f8598558ca0c8c3cb1d996219352946e849d270a61372bc93c8fbe9676e78e3ffd0dea07 + languageName: node + linkType: hard + +"@babel/plugin-transform-reserved-words@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-reserved-words@npm:7.18.6" + dependencies: + "@babel/helper-plugin-utils": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 0738cdc30abdae07c8ec4b233b30c31f68b3ff0eaa40eddb45ae607c066127f5fa99ddad3c0177d8e2832e3a7d3ad115775c62b431ebd6189c40a951b867a80c + languageName: node + linkType: hard + +"@babel/plugin-transform-shorthand-properties@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-shorthand-properties@npm:7.18.6" + dependencies: + "@babel/helper-plugin-utils": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: b8e4e8acc2700d1e0d7d5dbfd4fdfb935651913de6be36e6afb7e739d8f9ca539a5150075a0f9b79c88be25ddf45abb912fe7abf525f0b80f5b9d9860de685d7 + languageName: node + linkType: hard + +"@babel/plugin-transform-spread@npm:^7.19.0": + version: 7.20.7 + resolution: "@babel/plugin-transform-spread@npm:7.20.7" + dependencies: + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/helper-skip-transparent-expression-wrappers": ^7.20.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 8ea698a12da15718aac7489d4cde10beb8a3eea1f66167d11ab1e625033641e8b328157fd1a0b55dd6531933a160c01fc2e2e61132a385cece05f26429fd0cc2 + languageName: node + linkType: hard + +"@babel/plugin-transform-sticky-regex@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-sticky-regex@npm:7.18.6" + dependencies: + "@babel/helper-plugin-utils": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 68ea18884ae9723443ffa975eb736c8c0d751265859cd3955691253f7fee37d7a0f7efea96c8a062876af49a257a18ea0ed5fea0d95a7b3611ce40f7ee23aee3 + languageName: node + linkType: hard + +"@babel/plugin-transform-template-literals@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/plugin-transform-template-literals@npm:7.18.9" + dependencies: + "@babel/helper-plugin-utils": ^7.18.9 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 3d2fcd79b7c345917f69b92a85bdc3ddd68ce2c87dc70c7d61a8373546ccd1f5cb8adc8540b49dfba08e1b82bb7b3bbe23a19efdb2b9c994db2db42906ca9fb2 + languageName: node + linkType: hard + +"@babel/plugin-transform-typeof-symbol@npm:^7.18.9": + version: 7.18.9 + resolution: "@babel/plugin-transform-typeof-symbol@npm:7.18.9" + dependencies: + "@babel/helper-plugin-utils": ^7.18.9 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: e754e0d8b8a028c52e10c148088606e3f7a9942c57bd648fc0438e5b4868db73c386a5ed47ab6d6f0594aae29ee5ffc2ffc0f7ebee7fae560a066d6dea811cd4 + languageName: node + linkType: hard + +"@babel/plugin-transform-unicode-escapes@npm:^7.18.10": + version: 7.18.10 + resolution: "@babel/plugin-transform-unicode-escapes@npm:7.18.10" + dependencies: + "@babel/helper-plugin-utils": ^7.18.9 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: f5baca55cb3c11bc08ec589f5f522d85c1ab509b4d11492437e45027d64ae0b22f0907bd1381e8d7f2a436384bb1f9ad89d19277314242c5c2671a0f91d0f9cd + languageName: node + linkType: hard + +"@babel/plugin-transform-unicode-regex@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-transform-unicode-regex@npm:7.18.6" + dependencies: + "@babel/helper-create-regexp-features-plugin": ^7.18.6 + "@babel/helper-plugin-utils": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: d9e18d57536a2d317fb0b7c04f8f55347f3cfacb75e636b4c6fa2080ab13a3542771b5120e726b598b815891fc606d1472ac02b749c69fd527b03847f22dc25e + languageName: node + linkType: hard + +"@babel/preset-env@npm:^7.20.2": + version: 7.20.2 + resolution: "@babel/preset-env@npm:7.20.2" + dependencies: + "@babel/compat-data": ^7.20.1 + "@babel/helper-compilation-targets": ^7.20.0 + "@babel/helper-plugin-utils": ^7.20.2 + "@babel/helper-validator-option": ^7.18.6 + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ^7.18.6 + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": ^7.18.9 + "@babel/plugin-proposal-async-generator-functions": ^7.20.1 + "@babel/plugin-proposal-class-properties": ^7.18.6 + "@babel/plugin-proposal-class-static-block": ^7.18.6 + "@babel/plugin-proposal-dynamic-import": ^7.18.6 + "@babel/plugin-proposal-export-namespace-from": ^7.18.9 + "@babel/plugin-proposal-json-strings": ^7.18.6 + "@babel/plugin-proposal-logical-assignment-operators": ^7.18.9 + "@babel/plugin-proposal-nullish-coalescing-operator": ^7.18.6 + "@babel/plugin-proposal-numeric-separator": ^7.18.6 + "@babel/plugin-proposal-object-rest-spread": ^7.20.2 + "@babel/plugin-proposal-optional-catch-binding": ^7.18.6 + "@babel/plugin-proposal-optional-chaining": ^7.18.9 + "@babel/plugin-proposal-private-methods": ^7.18.6 + "@babel/plugin-proposal-private-property-in-object": ^7.18.6 + "@babel/plugin-proposal-unicode-property-regex": ^7.18.6 + "@babel/plugin-syntax-async-generators": ^7.8.4 + "@babel/plugin-syntax-class-properties": ^7.12.13 + "@babel/plugin-syntax-class-static-block": ^7.14.5 + "@babel/plugin-syntax-dynamic-import": ^7.8.3 + "@babel/plugin-syntax-export-namespace-from": ^7.8.3 + "@babel/plugin-syntax-import-assertions": ^7.20.0 + "@babel/plugin-syntax-json-strings": ^7.8.3 + "@babel/plugin-syntax-logical-assignment-operators": ^7.10.4 + "@babel/plugin-syntax-nullish-coalescing-operator": ^7.8.3 + "@babel/plugin-syntax-numeric-separator": ^7.10.4 + "@babel/plugin-syntax-object-rest-spread": ^7.8.3 + "@babel/plugin-syntax-optional-catch-binding": ^7.8.3 + "@babel/plugin-syntax-optional-chaining": ^7.8.3 + "@babel/plugin-syntax-private-property-in-object": ^7.14.5 + "@babel/plugin-syntax-top-level-await": ^7.14.5 + "@babel/plugin-transform-arrow-functions": ^7.18.6 + "@babel/plugin-transform-async-to-generator": ^7.18.6 + "@babel/plugin-transform-block-scoped-functions": ^7.18.6 + "@babel/plugin-transform-block-scoping": ^7.20.2 + "@babel/plugin-transform-classes": ^7.20.2 + "@babel/plugin-transform-computed-properties": ^7.18.9 + "@babel/plugin-transform-destructuring": ^7.20.2 + "@babel/plugin-transform-dotall-regex": ^7.18.6 + "@babel/plugin-transform-duplicate-keys": ^7.18.9 + "@babel/plugin-transform-exponentiation-operator": ^7.18.6 + "@babel/plugin-transform-for-of": ^7.18.8 + "@babel/plugin-transform-function-name": ^7.18.9 + "@babel/plugin-transform-literals": ^7.18.9 + "@babel/plugin-transform-member-expression-literals": ^7.18.6 + "@babel/plugin-transform-modules-amd": ^7.19.6 + "@babel/plugin-transform-modules-commonjs": ^7.19.6 + "@babel/plugin-transform-modules-systemjs": ^7.19.6 + "@babel/plugin-transform-modules-umd": ^7.18.6 + "@babel/plugin-transform-named-capturing-groups-regex": ^7.19.1 + "@babel/plugin-transform-new-target": ^7.18.6 + "@babel/plugin-transform-object-super": ^7.18.6 + "@babel/plugin-transform-parameters": ^7.20.1 + "@babel/plugin-transform-property-literals": ^7.18.6 + "@babel/plugin-transform-regenerator": ^7.18.6 + "@babel/plugin-transform-reserved-words": ^7.18.6 + "@babel/plugin-transform-shorthand-properties": ^7.18.6 + "@babel/plugin-transform-spread": ^7.19.0 + "@babel/plugin-transform-sticky-regex": ^7.18.6 + "@babel/plugin-transform-template-literals": ^7.18.9 + "@babel/plugin-transform-typeof-symbol": ^7.18.9 + "@babel/plugin-transform-unicode-escapes": ^7.18.10 + "@babel/plugin-transform-unicode-regex": ^7.18.6 + "@babel/preset-modules": ^0.1.5 + "@babel/types": ^7.20.2 + babel-plugin-polyfill-corejs2: ^0.3.3 + babel-plugin-polyfill-corejs3: ^0.6.0 + babel-plugin-polyfill-regenerator: ^0.4.1 + core-js-compat: ^3.25.1 + semver: ^6.3.0 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: ece2d7e9c7789db6116e962b8e1a55eb55c110c44c217f0c8f6ffea4ca234954e66557f7bd019b7affadf7fbb3a53ccc807e93fc935aacd48146234b73b6947e + languageName: node + linkType: hard + +"@babel/preset-modules@npm:^0.1.5": + version: 0.1.5 + resolution: "@babel/preset-modules@npm:0.1.5" + dependencies: + "@babel/helper-plugin-utils": ^7.0.0 + "@babel/plugin-proposal-unicode-property-regex": ^7.4.4 + "@babel/plugin-transform-dotall-regex": ^7.4.4 + "@babel/types": ^7.4.4 + esutils: ^2.0.2 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 8430e0e9e9d520b53e22e8c4c6a5a080a12b63af6eabe559c2310b187bd62ae113f3da82ba33e9d1d0f3230930ca702843aae9dd226dec51f7d7114dc1f51c10 + languageName: node + linkType: hard + +"@babel/preset-react@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/preset-react@npm:7.18.6" + dependencies: + "@babel/helper-plugin-utils": ^7.18.6 + "@babel/helper-validator-option": ^7.18.6 + "@babel/plugin-transform-react-display-name": ^7.18.6 + "@babel/plugin-transform-react-jsx": ^7.18.6 + "@babel/plugin-transform-react-jsx-development": ^7.18.6 + "@babel/plugin-transform-react-pure-annotations": ^7.18.6 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 540d9cf0a0cc0bb07e6879994e6fb7152f87dafbac880b56b65e2f528134c7ba33e0cd140b58700c77b2ebf4c81fa6468fed0ba391462d75efc7f8c1699bb4c3 + languageName: node + linkType: hard + +"@babel/regjsgen@npm:^0.8.0": + version: 0.8.0 + resolution: "@babel/regjsgen@npm:0.8.0" + checksum: 89c338fee774770e5a487382170711014d49a68eb281e74f2b5eac88f38300a4ad545516a7786a8dd5702e9cf009c94c2f582d200f077ac5decd74c56b973730 + languageName: node + linkType: hard + +"@babel/runtime@npm:^7.1.2": + version: 7.14.8 + resolution: "@babel/runtime@npm:7.14.8" + dependencies: + regenerator-runtime: ^0.13.4 + checksum: d2dd0ce51ddab78ac93928b04042425145d0dc8cc2b70150d47934f8703f55702eb0b2894f9bd47f66794ad04d8bb03a6a847d0138fbb7aa0b970b5ccd5cc8b7 + languageName: node + linkType: hard + +"@babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.8.7": + version: 7.21.0 + resolution: "@babel/runtime@npm:7.21.0" + dependencies: + regenerator-runtime: ^0.13.11 + checksum: 7b33e25bfa9e0e1b9e8828bb61b2d32bdd46b41b07ba7cb43319ad08efc6fda8eb89445193e67d6541814627df0ca59122c0ea795e412b99c5183a0540d338ab + languageName: node + linkType: hard + +"@babel/runtime@npm:^7.8.4": + version: 7.18.3 + resolution: "@babel/runtime@npm:7.18.3" + dependencies: + regenerator-runtime: ^0.13.4 + checksum: db8526226aa02cfa35a5a7ac1a34b5f303c62a1f000c7db48cb06c6290e616483e5036ab3c4e7a84d0f3be6d4e2148d5fe5cec9564bf955f505c3e764b83d7f1 + languageName: node + linkType: hard + +"@babel/template@npm:^7.18.10, @babel/template@npm:^7.20.7": + version: 7.20.7 + resolution: "@babel/template@npm:7.20.7" + dependencies: + "@babel/code-frame": ^7.18.6 + "@babel/parser": ^7.20.7 + "@babel/types": ^7.20.7 + checksum: 2eb1a0ab8d415078776bceb3473d07ab746e6bb4c2f6ca46ee70efb284d75c4a32bb0cd6f4f4946dec9711f9c0780e8e5d64b743208deac6f8e9858afadc349e + languageName: node + linkType: hard + +"@babel/traverse@npm:^7.20.5, @babel/traverse@npm:^7.20.7, @babel/traverse@npm:^7.21.0, @babel/traverse@npm:^7.21.2, @babel/traverse@npm:^7.21.3": + version: 7.21.3 + resolution: "@babel/traverse@npm:7.21.3" + dependencies: + "@babel/code-frame": ^7.18.6 + "@babel/generator": ^7.21.3 + "@babel/helper-environment-visitor": ^7.18.9 + "@babel/helper-function-name": ^7.21.0 + "@babel/helper-hoist-variables": ^7.18.6 + "@babel/helper-split-export-declaration": ^7.18.6 + "@babel/parser": ^7.21.3 + "@babel/types": ^7.21.3 + debug: ^4.1.0 + globals: ^11.1.0 + checksum: 0af5bcd47a2fc501592b90ac1feae9d449afb9ab0772a4f6e68230f4cd3a475795d538c1de3f880fe3414b6c2820bac84d02c6549eea796f39d74a603717447b + languageName: node + linkType: hard + +"@babel/types@npm:^7.16.7, @babel/types@npm:^7.4.4": + version: 7.18.4 + resolution: "@babel/types@npm:7.18.4" + dependencies: + "@babel/helper-validator-identifier": ^7.16.7 + to-fast-properties: ^2.0.0 + checksum: 85df59beb99c1b95e9e41590442f2ffa1e5b1b558d025489db40c9f7c906bd03a17da26c3ec486e5800e80af27c42ca7eee9506d9212ab17766d2d68d30fbf52 + languageName: node + linkType: hard + +"@babel/types@npm:^7.18.6, @babel/types@npm:^7.18.9, @babel/types@npm:^7.20.0, @babel/types@npm:^7.20.2, @babel/types@npm:^7.20.5, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.0, @babel/types@npm:^7.21.2, @babel/types@npm:^7.21.3, @babel/types@npm:^7.8.3": + version: 7.21.3 + resolution: "@babel/types@npm:7.21.3" + dependencies: + "@babel/helper-string-parser": ^7.19.4 + "@babel/helper-validator-identifier": ^7.19.1 + to-fast-properties: ^2.0.0 + checksum: b750274718ba9cefd0b81836c464009bb6ba339fccce51b9baff497a0a2d96c044c61dc90cf203cec0adc770454b53a9681c3f7716883c802b85ab84c365ba35 + languageName: node + linkType: hard + +"@gar/promisify@npm:^1.1.3": + version: 1.1.3 + resolution: "@gar/promisify@npm:1.1.3" + checksum: 4059f790e2d07bf3c3ff3e0fec0daa8144fe35c1f6e0111c9921bd32106adaa97a4ab096ad7dab1e28ee6a9060083c4d1a4ada42a7f5f3f7a96b8812e2b757c1 + languageName: node + linkType: hard + +"@jridgewell/gen-mapping@npm:^0.1.0": + version: 0.1.1 + resolution: "@jridgewell/gen-mapping@npm:0.1.1" + dependencies: + "@jridgewell/set-array": ^1.0.0 + "@jridgewell/sourcemap-codec": ^1.4.10 + checksum: 3bcc21fe786de6ffbf35c399a174faab05eb23ce6a03e8769569de28abbf4facc2db36a9ddb0150545ae23a8d35a7cf7237b2aa9e9356a7c626fb4698287d5cc + languageName: node + linkType: hard + +"@jridgewell/gen-mapping@npm:^0.3.0": + version: 0.3.1 + resolution: "@jridgewell/gen-mapping@npm:0.3.1" + dependencies: + "@jridgewell/set-array": ^1.0.0 + "@jridgewell/sourcemap-codec": ^1.4.10 + "@jridgewell/trace-mapping": ^0.3.9 + checksum: e9e7bb3335dea9e60872089761d4e8e089597360cdb1af90370e9d53b7d67232c1e0a3ab65fbfef4fc785745193fbc56bff9f3a6cab6c6ce3f15e12b4191f86b + languageName: node + linkType: hard + +"@jridgewell/gen-mapping@npm:^0.3.2": + version: 0.3.2 + resolution: "@jridgewell/gen-mapping@npm:0.3.2" + dependencies: + "@jridgewell/set-array": ^1.0.1 + "@jridgewell/sourcemap-codec": ^1.4.10 + "@jridgewell/trace-mapping": ^0.3.9 + checksum: 1832707a1c476afebe4d0fbbd4b9434fdb51a4c3e009ab1e9938648e21b7a97049fa6009393bdf05cab7504108413441df26d8a3c12193996e65493a4efb6882 + languageName: node + linkType: hard + +"@jridgewell/resolve-uri@npm:3.1.0": + version: 3.1.0 + resolution: "@jridgewell/resolve-uri@npm:3.1.0" + checksum: b5ceaaf9a110fcb2780d1d8f8d4a0bfd216702f31c988d8042e5f8fbe353c55d9b0f55a1733afdc64806f8e79c485d2464680ac48a0d9fcadb9548ee6b81d267 + languageName: node + linkType: hard + +"@jridgewell/resolve-uri@npm:^3.0.3": + version: 3.0.7 + resolution: "@jridgewell/resolve-uri@npm:3.0.7" + checksum: 94f454f4cef8f0acaad85745fd3ca6cd0d62ef731cf9f952ecb89b8b2ce5e20998cd52be31311cedc5fa5b28b1708a15f3ad9df0fe1447ee4f42959b036c4b5b + languageName: node + linkType: hard + +"@jridgewell/set-array@npm:^1.0.0": + version: 1.1.1 + resolution: "@jridgewell/set-array@npm:1.1.1" + checksum: cc5d91e0381c347e3edee4ca90b3c292df9e6e55f29acbe0dd97de8651b4730e9ab761406fd572effa79972a0edc55647b627f8c72315e276d959508853d9bf2 + languageName: node + linkType: hard + +"@jridgewell/set-array@npm:^1.0.1": + version: 1.1.2 + resolution: "@jridgewell/set-array@npm:1.1.2" + checksum: 69a84d5980385f396ff60a175f7177af0b8da4ddb81824cb7016a9ef914eee9806c72b6b65942003c63f7983d4f39a5c6c27185bbca88eb4690b62075602e28e + languageName: node + linkType: hard + +"@jridgewell/source-map@npm:^0.3.2": + version: 0.3.2 + resolution: "@jridgewell/source-map@npm:0.3.2" + dependencies: + "@jridgewell/gen-mapping": ^0.3.0 + "@jridgewell/trace-mapping": ^0.3.9 + checksum: 1b83f0eb944e77b70559a394d5d3b3f98a81fcc186946aceb3ef42d036762b52ef71493c6c0a3b7c1d2f08785f53ba2df1277fe629a06e6109588ff4cdcf7482 + languageName: node + linkType: hard + +"@jridgewell/sourcemap-codec@npm:1.4.14": + version: 1.4.14 + resolution: "@jridgewell/sourcemap-codec@npm:1.4.14" + checksum: 61100637b6d173d3ba786a5dff019e1a74b1f394f323c1fee337ff390239f053b87266c7a948777f4b1ee68c01a8ad0ab61e5ff4abb5a012a0b091bec391ab97 + languageName: node + linkType: hard + +"@jridgewell/sourcemap-codec@npm:^1.4.10": + version: 1.4.13 + resolution: "@jridgewell/sourcemap-codec@npm:1.4.13" + checksum: f14449096f60a5f921262322fef65ce0bbbfb778080b3b20212080bcefdeba621c43a58c27065bd536ecb4cc767b18eb9c45f15b6b98a4970139572b60603a1c + languageName: node + linkType: hard + +"@jridgewell/trace-mapping@npm:^0.3.17": + version: 0.3.17 + resolution: "@jridgewell/trace-mapping@npm:0.3.17" + dependencies: + "@jridgewell/resolve-uri": 3.1.0 + "@jridgewell/sourcemap-codec": 1.4.14 + checksum: 9d703b859cff5cd83b7308fd457a431387db5db96bd781a63bf48e183418dd9d3d44e76b9e4ae13237f6abeeb25d739ec9215c1d5bfdd08f66f750a50074a339 + languageName: node + linkType: hard + +"@jridgewell/trace-mapping@npm:^0.3.9": + version: 0.3.13 + resolution: "@jridgewell/trace-mapping@npm:0.3.13" + dependencies: + "@jridgewell/resolve-uri": ^3.0.3 + "@jridgewell/sourcemap-codec": ^1.4.10 + checksum: e38254e830472248ca10a6ed1ae75af5e8514f0680245a5e7b53bc3c030fd8691d4d3115d80595b45d3badead68269769ed47ecbbdd67db1343a11f05700e75a + languageName: node + linkType: hard + +"@lezer/common@npm:^0.15.0, @lezer/common@npm:^0.15.7": + version: 0.15.12 + resolution: "@lezer/common@npm:0.15.12" + checksum: dae65816187bd690bf446bec116313d3b5328e70e3e1f7c806273d9356ca2017cf82aa650ea53b95260fb98898ea73d44f33319f9dbbd48d473e2f20771b2377 + languageName: node + linkType: hard + +"@lezer/lr@npm:^0.15.4": + version: 0.15.8 + resolution: "@lezer/lr@npm:0.15.8" + dependencies: + "@lezer/common": ^0.15.0 + checksum: e741225d6ac9cf08f8016bad49622fbd4a4e0d20c2e8c2b38a0abf0ddca69c58275b0ebdb9d5dde2905cf84f6977bc302f7ed5e5ba42c23afa27e9e65b900f36 + languageName: node + linkType: hard + +"@lmdb/lmdb-darwin-arm64@npm:2.5.2": + version: 2.5.2 + resolution: "@lmdb/lmdb-darwin-arm64@npm:2.5.2" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@lmdb/lmdb-darwin-x64@npm:2.5.2": + version: 2.5.2 + resolution: "@lmdb/lmdb-darwin-x64@npm:2.5.2" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@lmdb/lmdb-linux-arm64@npm:2.5.2": + version: 2.5.2 + resolution: "@lmdb/lmdb-linux-arm64@npm:2.5.2" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@lmdb/lmdb-linux-arm@npm:2.5.2": + version: 2.5.2 + resolution: "@lmdb/lmdb-linux-arm@npm:2.5.2" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@lmdb/lmdb-linux-x64@npm:2.5.2": + version: 2.5.2 + resolution: "@lmdb/lmdb-linux-x64@npm:2.5.2" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@lmdb/lmdb-win32-x64@npm:2.5.2": + version: 2.5.2 + resolution: "@lmdb/lmdb-win32-x64@npm:2.5.2" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@mischnic/json-sourcemap@npm:^0.1.0": + version: 0.1.0 + resolution: "@mischnic/json-sourcemap@npm:0.1.0" + dependencies: + "@lezer/common": ^0.15.7 + "@lezer/lr": ^0.15.4 + json5: ^2.2.1 + checksum: a30eda9eb02db5213b7aa2dc3c688257884a8969849ffa5a3a7c64c5f2a1cfed06691d94f02b37294a3a3b9efe7f88ee6b86c9ef20a799af54807ff2de2d253e + languageName: node + linkType: hard + +"@msgpackr-extract/msgpackr-extract-darwin-arm64@npm:2.0.2": + version: 2.0.2 + resolution: "@msgpackr-extract/msgpackr-extract-darwin-arm64@npm:2.0.2" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@msgpackr-extract/msgpackr-extract-darwin-x64@npm:2.0.2": + version: 2.0.2 + resolution: "@msgpackr-extract/msgpackr-extract-darwin-x64@npm:2.0.2" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@msgpackr-extract/msgpackr-extract-linux-arm64@npm:2.0.2": + version: 2.0.2 + resolution: "@msgpackr-extract/msgpackr-extract-linux-arm64@npm:2.0.2" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@msgpackr-extract/msgpackr-extract-linux-arm@npm:2.0.2": + version: 2.0.2 + resolution: "@msgpackr-extract/msgpackr-extract-linux-arm@npm:2.0.2" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@msgpackr-extract/msgpackr-extract-linux-x64@npm:2.0.2": + version: 2.0.2 + resolution: "@msgpackr-extract/msgpackr-extract-linux-x64@npm:2.0.2" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@msgpackr-extract/msgpackr-extract-win32-x64@npm:2.0.2": + version: 2.0.2 + resolution: "@msgpackr-extract/msgpackr-extract-win32-x64@npm:2.0.2" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@npmcli/fs@npm:^2.1.0": + version: 2.1.2 + resolution: "@npmcli/fs@npm:2.1.2" + dependencies: + "@gar/promisify": ^1.1.3 + semver: ^7.3.5 + checksum: 405074965e72d4c9d728931b64d2d38e6ea12066d4fad651ac253d175e413c06fe4350970c783db0d749181da8fe49c42d3880bd1cbc12cd68e3a7964d820225 + languageName: node + linkType: hard + +"@npmcli/move-file@npm:^2.0.0": + version: 2.0.1 + resolution: "@npmcli/move-file@npm:2.0.1" + dependencies: + mkdirp: ^1.0.4 + rimraf: ^3.0.2 + checksum: 52dc02259d98da517fae4cb3a0a3850227bdae4939dda1980b788a7670636ca2b4a01b58df03dd5f65c1e3cb70c50fa8ce5762b582b3f499ec30ee5ce1fd9380 + languageName: node + linkType: hard + +"@parcel/bundler-default@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/bundler-default@npm:2.8.3" + dependencies: + "@parcel/diagnostic": 2.8.3 + "@parcel/graph": 2.8.3 + "@parcel/hash": 2.8.3 + "@parcel/plugin": 2.8.3 + "@parcel/utils": 2.8.3 + nullthrows: ^1.1.1 + checksum: 219b2be341cad20991659b7a3031454a081ce0787c161a4da8a73ae8a4af4468667b284caea9488e869b162763d308cfd6495ab35fe386413b14325d6667ea86 + languageName: node + linkType: hard + +"@parcel/cache@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/cache@npm:2.8.3" + dependencies: + "@parcel/fs": 2.8.3 + "@parcel/logger": 2.8.3 + "@parcel/utils": 2.8.3 + lmdb: 2.5.2 + peerDependencies: + "@parcel/core": ^2.8.3 + checksum: cd679053d229f8d06536a8fc9d857e5fa58905492e1a97c4f6b1da82de0dcef202a609c1e36206d3cdb32e5da3a214525f868b98dfd7aa671a53dacceb004fd9 + languageName: node + linkType: hard + +"@parcel/codeframe@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/codeframe@npm:2.8.3" + dependencies: + chalk: ^4.1.0 + checksum: a6e82c30e6251dcae14f247a14f6cb265f766b8bf18b62dd6a1c4a103cfae364a08897b36c5c379d0d867169647cb72962266f77571f718ff68ef70a16b81c02 + languageName: node + linkType: hard + +"@parcel/compressor-raw@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/compressor-raw@npm:2.8.3" + dependencies: + "@parcel/plugin": 2.8.3 + checksum: ca3b8a4f60e5193cffaa8041e709513df9c6cb54f32c9d20fef993a9af2d84f1e2d8bf8f4092220a8abaec24679498f854e683511876187f35b4f94a5852cf85 + languageName: node + linkType: hard + +"@parcel/config-default@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/config-default@npm:2.8.3" + dependencies: + "@parcel/bundler-default": 2.8.3 + "@parcel/compressor-raw": 2.8.3 + "@parcel/namer-default": 2.8.3 + "@parcel/optimizer-css": 2.8.3 + "@parcel/optimizer-htmlnano": 2.8.3 + "@parcel/optimizer-image": 2.8.3 + "@parcel/optimizer-svgo": 2.8.3 + "@parcel/optimizer-terser": 2.8.3 + "@parcel/packager-css": 2.8.3 + "@parcel/packager-html": 2.8.3 + "@parcel/packager-js": 2.8.3 + "@parcel/packager-raw": 2.8.3 + "@parcel/packager-svg": 2.8.3 + "@parcel/reporter-dev-server": 2.8.3 + "@parcel/resolver-default": 2.8.3 + "@parcel/runtime-browser-hmr": 2.8.3 + "@parcel/runtime-js": 2.8.3 + "@parcel/runtime-react-refresh": 2.8.3 + "@parcel/runtime-service-worker": 2.8.3 + "@parcel/transformer-babel": 2.8.3 + "@parcel/transformer-css": 2.8.3 + "@parcel/transformer-html": 2.8.3 + "@parcel/transformer-image": 2.8.3 + "@parcel/transformer-js": 2.8.3 + "@parcel/transformer-json": 2.8.3 + "@parcel/transformer-postcss": 2.8.3 + "@parcel/transformer-posthtml": 2.8.3 + "@parcel/transformer-raw": 2.8.3 + "@parcel/transformer-react-refresh-wrap": 2.8.3 + "@parcel/transformer-svg": 2.8.3 + peerDependencies: + "@parcel/core": ^2.8.3 + checksum: 08c700a7a253f39e84e1d341b3e0f558a2410bb27bf8a128113d8d157c32a7ef6b6ebd95e2c26d9f35c1040b98ff229ab56782247746189b4c41b925d1efd251 + languageName: node + linkType: hard + +"@parcel/core@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/core@npm:2.8.3" + dependencies: + "@mischnic/json-sourcemap": ^0.1.0 + "@parcel/cache": 2.8.3 + "@parcel/diagnostic": 2.8.3 + "@parcel/events": 2.8.3 + "@parcel/fs": 2.8.3 + "@parcel/graph": 2.8.3 + "@parcel/hash": 2.8.3 + "@parcel/logger": 2.8.3 + "@parcel/package-manager": 2.8.3 + "@parcel/plugin": 2.8.3 + "@parcel/source-map": ^2.1.1 + "@parcel/types": 2.8.3 + "@parcel/utils": 2.8.3 + "@parcel/workers": 2.8.3 + abortcontroller-polyfill: ^1.1.9 + base-x: ^3.0.8 + browserslist: ^4.6.6 + clone: ^2.1.1 + dotenv: ^7.0.0 + dotenv-expand: ^5.1.0 + json5: ^2.2.0 + msgpackr: ^1.5.4 + nullthrows: ^1.1.1 + semver: ^5.7.1 + checksum: 68adceb1b041208fe922bb52da218e6be90d6e016322f4eac5a5dbfbac72838080cf9bbce51785d65556a258293c02dffba4482217dbd9b723258101d925fb0e + languageName: node + linkType: hard + +"@parcel/css-linux-arm64-gnu@npm:^1.13.1": + version: 1.13.1 + resolution: "@parcel/css-linux-arm64-gnu@npm:1.13.1" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@parcel/diagnostic@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/diagnostic@npm:2.8.3" + dependencies: + "@mischnic/json-sourcemap": ^0.1.0 + nullthrows: ^1.1.1 + checksum: c24d98a2dbf068ef334c595d51504cd063310c0327477b5d7bcf817af3f8ad79d56593cdf91d8d45cb4a41a48baf9090ae4100a96d2c197d4ed20bc5db9df2d9 + languageName: node + linkType: hard + +"@parcel/events@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/events@npm:2.8.3" + checksum: 9d23c6663e9afce1d1094c46d38eba0b0171835201140258c1dcd33f63cfbc20bb1abdc163cbb7a01d407a8cf06c8742c10035c8a835ebca261b19d8ee0fbf7e + languageName: node + linkType: hard + +"@parcel/fs-search@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/fs-search@npm:2.8.3" + dependencies: + detect-libc: ^1.0.3 + checksum: 25e8eda6942fbf28e02cef1f5e94acafb3e33275a20b0a3e553402f04d2d24026be796b645728e872949dc8555b03a7d26d615a4f8eeed03a3af76aed535cc10 + languageName: node + linkType: hard + +"@parcel/fs@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/fs@npm:2.8.3" + dependencies: + "@parcel/fs-search": 2.8.3 + "@parcel/types": 2.8.3 + "@parcel/utils": 2.8.3 + "@parcel/watcher": ^2.0.7 + "@parcel/workers": 2.8.3 + peerDependencies: + "@parcel/core": ^2.8.3 + checksum: cc421552daef3c7676030867a1b4ed45d64d5f4221b0b12d487a86183a39544290fd3e7ed9104b1b58c05f2a6b5ec0698ce37a9cd49c484d94ed6b445f26d598 + languageName: node + linkType: hard + +"@parcel/graph@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/graph@npm:2.8.3" + dependencies: + nullthrows: ^1.1.1 + checksum: ceed8445f5a23396cca001a54ee0620bd7d6ecbb455977c16bd2f446da14c1791817ed715a4cf70d6ba66310991eeee44d692f15f70ff52e75b98b629da25a88 + languageName: node + linkType: hard + +"@parcel/hash@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/hash@npm:2.8.3" + dependencies: + detect-libc: ^1.0.3 + xxhash-wasm: ^0.4.2 + checksum: 29cef199feda672756c930a8b45ee91e46607aa1b6659c38658758fe2f88870c20e0d4e8738d96ca8b44df60c1b767b5593110e2d24b99382214158c759258d0 + languageName: node + linkType: hard + +"@parcel/logger@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/logger@npm:2.8.3" + dependencies: + "@parcel/diagnostic": 2.8.3 + "@parcel/events": 2.8.3 + checksum: 04fd46313138ea8e38c5bd051cd79ee245ad0a7bb6d5d12a892cafa79755af81ec1b6ddc83a79224bb74170bc1323f016cf849981326adb391f43920976ec9dc + languageName: node + linkType: hard + +"@parcel/markdown-ansi@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/markdown-ansi@npm:2.8.3" + dependencies: + chalk: ^4.1.0 + checksum: 1985f149b2ac08347f954230922fdcc45d7ceedba9b7f458078843a018d950a56cb512fb951537b4f995e861b9290b0757cfc0eadf542a13b124175b5ef02945 + languageName: node + linkType: hard + +"@parcel/namer-default@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/namer-default@npm:2.8.3" + dependencies: + "@parcel/diagnostic": 2.8.3 + "@parcel/plugin": 2.8.3 + nullthrows: ^1.1.1 + checksum: 7c2c3434460d8fa6c9d482a9bfc681e47322ad82c8beef193eee9e45831374860d0f89de4c69e2e5cf41301cad19c7e87f5b536ca7d684aa383e783bcce02ef1 + languageName: node + linkType: hard + +"@parcel/node-resolver-core@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/node-resolver-core@npm:2.8.3" + dependencies: + "@parcel/diagnostic": 2.8.3 + "@parcel/utils": 2.8.3 + nullthrows: ^1.1.1 + semver: ^5.7.1 + checksum: 4976d3ecc9acc6ee05c7709291f4576c269bc84f896c8bf9e6171ce6f9fbd9c2dd7e3db4e11542b3b29093c73f5451724c94bf7b0735b9920ddcdeecf1809968 + languageName: node + linkType: hard + +"@parcel/optimizer-css@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/optimizer-css@npm:2.8.3" + dependencies: + "@parcel/diagnostic": 2.8.3 + "@parcel/plugin": 2.8.3 + "@parcel/source-map": ^2.1.1 + "@parcel/utils": 2.8.3 + browserslist: ^4.6.6 + lightningcss: ^1.16.1 + nullthrows: ^1.1.1 + checksum: ffac43a2c20243d57b8627257b5a74462ebc0f4aa780b3117237240c9c3e9ca37ddcc8312296be9fe571a78f5a44cc14fa47ca9490d3796d673d8313d6cd8c9a + languageName: node + linkType: hard + +"@parcel/optimizer-htmlnano@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/optimizer-htmlnano@npm:2.8.3" + dependencies: + "@parcel/plugin": 2.8.3 + htmlnano: ^2.0.0 + nullthrows: ^1.1.1 + posthtml: ^0.16.5 + svgo: ^2.4.0 + checksum: ca1cab7b1ecc16f209ad867fbdd8b2f446fd831d8688db068491fa22786a5aa3a0debb4290e0f003830c6b06c6f3a4c3a3cd9cdb033e7fa6cded8a19887d5f23 + languageName: node + linkType: hard + +"@parcel/optimizer-image@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/optimizer-image@npm:2.8.3" + dependencies: + "@parcel/diagnostic": 2.8.3 + "@parcel/plugin": 2.8.3 + "@parcel/utils": 2.8.3 + "@parcel/workers": 2.8.3 + detect-libc: ^1.0.3 + checksum: 72c5acffaea833237f62e23c8fb183eb85863ccddeb11304b2299b28973b957daba1e34854d347314edf35d83cba695c0d7600e1ae125dec4cc3151abd8f2e31 + languageName: node + linkType: hard + +"@parcel/optimizer-svgo@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/optimizer-svgo@npm:2.8.3" + dependencies: + "@parcel/diagnostic": 2.8.3 + "@parcel/plugin": 2.8.3 + "@parcel/utils": 2.8.3 + svgo: ^2.4.0 + checksum: b3544c08fac4009de1ec6f88136a58cdec70b072433b13fb99f9e6584dc4731afea82ae13d27e4121ed5aaec9e4481225a54251ce52b6ece835908300c26fa33 + languageName: node + linkType: hard + +"@parcel/optimizer-terser@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/optimizer-terser@npm:2.8.3" + dependencies: + "@parcel/diagnostic": 2.8.3 + "@parcel/plugin": 2.8.3 + "@parcel/source-map": ^2.1.1 + "@parcel/utils": 2.8.3 + nullthrows: ^1.1.1 + terser: ^5.2.0 + checksum: ee1959f5965c7eee8ad1519f9d2554810030f326e959dd5e44aa014c29a51c2ab777dfbbf604a6b4436b75176a8694b7b8c9d99f945d57dea7828225762c8823 + languageName: node + linkType: hard + +"@parcel/package-manager@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/package-manager@npm:2.8.3" + dependencies: + "@parcel/diagnostic": 2.8.3 + "@parcel/fs": 2.8.3 + "@parcel/logger": 2.8.3 + "@parcel/types": 2.8.3 + "@parcel/utils": 2.8.3 + "@parcel/workers": 2.8.3 + semver: ^5.7.1 + peerDependencies: + "@parcel/core": ^2.8.3 + checksum: 572a5aacfd7bc545d9aa35ff2125f1231226b550f9b0fe2c36d68a82ec8ffb047035e25fdb883bc2331a6eaf69c98bb5d6752644546d962de7bf544c6243a959 + languageName: node + linkType: hard + +"@parcel/packager-css@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/packager-css@npm:2.8.3" + dependencies: + "@parcel/plugin": 2.8.3 + "@parcel/source-map": ^2.1.1 + "@parcel/utils": 2.8.3 + nullthrows: ^1.1.1 + checksum: bb28fc9f02df83a1fd8eac7043466debb67398190819282a40a52ff299b0f4c3f474bfa7806be776ce36a66cc89574128a9fa210d2c0c9cb905bbb4dbbd2b926 + languageName: node + linkType: hard + +"@parcel/packager-html@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/packager-html@npm:2.8.3" + dependencies: + "@parcel/plugin": 2.8.3 + "@parcel/types": 2.8.3 + "@parcel/utils": 2.8.3 + nullthrows: ^1.1.1 + posthtml: ^0.16.5 + checksum: 631f98fca0fdd3f11fe4cfbc1e0ad73b86f7fb00be7164fef5633c600a13282ae592b8f7d9aa31d4f66bd645ae57ce27e67db51a81b2a91c286ed5c8b36a4a87 + languageName: node + linkType: hard + +"@parcel/packager-js@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/packager-js@npm:2.8.3" + dependencies: + "@parcel/diagnostic": 2.8.3 + "@parcel/hash": 2.8.3 + "@parcel/plugin": 2.8.3 + "@parcel/source-map": ^2.1.1 + "@parcel/utils": 2.8.3 + globals: ^13.2.0 + nullthrows: ^1.1.1 + checksum: 92ac88244b6104c5905ab95d882b755134042654ab48106ca84ab18441fb7240b66f049e407146958aead0812345823da729a4a37f32be17afd2b44cbdebc926 + languageName: node + linkType: hard + +"@parcel/packager-raw@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/packager-raw@npm:2.8.3" + dependencies: + "@parcel/plugin": 2.8.3 + checksum: 26236dd64624a25fc1d749fb96b1bc3a6854b14d4386109670572f55feda4bb6affde19b1c9e971c4e50bfb53fd88e32da8303c83a3cb18ceaf12dd310685c34 + languageName: node + linkType: hard + +"@parcel/packager-svg@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/packager-svg@npm:2.8.3" + dependencies: + "@parcel/plugin": 2.8.3 + "@parcel/types": 2.8.3 + "@parcel/utils": 2.8.3 + posthtml: ^0.16.4 + checksum: 45c966ad8e6dbb25049adca66d761089a09cda83558d8767b46501a023b8d94b050f1a2899b1c8b18eac6cf87d2605ad5aa9a4cb2f9d90474794576dafb2e4fc + languageName: node + linkType: hard + +"@parcel/plugin@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/plugin@npm:2.8.3" + dependencies: + "@parcel/types": 2.8.3 + checksum: a69ac66f5cc28197cf689f1c4144398457d62a086621a22b3b45fe863909a094b616dad415ec01673a9eb731b05fe9060bcb340c07efcd48343577a540fbfdf7 + languageName: node + linkType: hard + +"@parcel/reporter-cli@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/reporter-cli@npm:2.8.3" + dependencies: + "@parcel/plugin": 2.8.3 + "@parcel/types": 2.8.3 + "@parcel/utils": 2.8.3 + chalk: ^4.1.0 + term-size: ^2.2.1 + checksum: 791dd4706aac706427a563455d9db5fa330b77e94fe4226b3751cd527327d8540d62400a8040e85a5fd29ecb6e673507e9d4a1fa754c093f1c005078670eef85 + languageName: node + linkType: hard + +"@parcel/reporter-dev-server@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/reporter-dev-server@npm:2.8.3" + dependencies: + "@parcel/plugin": 2.8.3 + "@parcel/utils": 2.8.3 + checksum: 329db9fd0cdc3ddc36d8156a7d67747335c76b1368116c98e266218f1e1ce4ea108981441bcb78961f64e2067a2d8a1745d8aa069398d50e67278e1333293723 + languageName: node + linkType: hard + +"@parcel/resolver-default@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/resolver-default@npm:2.8.3" + dependencies: + "@parcel/node-resolver-core": 2.8.3 + "@parcel/plugin": 2.8.3 + checksum: 40515a62c1a301050144e1427ac7a591afedea50e89baff0ab4ed05ad8424f5df6ad4a7b5e413956a199ecef18bf8220b353fb115af72fac4187a62e8a997d1d + languageName: node + linkType: hard + +"@parcel/runtime-browser-hmr@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/runtime-browser-hmr@npm:2.8.3" + dependencies: + "@parcel/plugin": 2.8.3 + "@parcel/utils": 2.8.3 + checksum: 56c276c7b03bb4c301d7dbe94c5183404af4064286f67682399e848ff894bfb5ea783dad11082290d40f2f07be64252dd236b993baf2e3e8fbb30a572f95a0dc + languageName: node + linkType: hard + +"@parcel/runtime-js@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/runtime-js@npm:2.8.3" + dependencies: + "@parcel/plugin": 2.8.3 + "@parcel/utils": 2.8.3 + nullthrows: ^1.1.1 + checksum: ee5e04f84d522a6f53753c3956d37cacb2bdabb2539e2f40e640762b3cc43b20efc495331fe254d92d82a06c3e1b4690c17125090a12300d75ad7c3a9ca3e2f0 + languageName: node + linkType: hard + +"@parcel/runtime-react-refresh@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/runtime-react-refresh@npm:2.8.3" + dependencies: + "@parcel/plugin": 2.8.3 + "@parcel/utils": 2.8.3 + react-error-overlay: 6.0.9 + react-refresh: ^0.9.0 + checksum: 327159be0c8183f1cff139de973e8e8ca6b83dc2fc94846a89415fabf8cd8535e95ed3ae9750ac08e73a303de57c18c4e5da959ecbe73af75f1d3c9a98f5c20b + languageName: node + linkType: hard + +"@parcel/runtime-service-worker@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/runtime-service-worker@npm:2.8.3" + dependencies: + "@parcel/plugin": 2.8.3 + "@parcel/utils": 2.8.3 + nullthrows: ^1.1.1 + checksum: 0646fee9a9378c0844c223d0eaf1c46e817738e70b2e993434717fb6aab998339b37a32c5bd9db891fcb8bc44dc3d7530564f84a5cd978d6dd47f204f18bd44a + languageName: node + linkType: hard + +"@parcel/source-map@npm:^2.1.1": + version: 2.1.1 + resolution: "@parcel/source-map@npm:2.1.1" + dependencies: + detect-libc: ^1.0.3 + checksum: 1fa27a7047ec08faf7fe1dd0e2ae95a27b84697ecfaed029d0b7d06e46d84ed8f98a9dc9d308fe623655f3c985052dcf7622de479bfa6103c44884fb7f6c810a + languageName: node + linkType: hard + +"@parcel/transformer-babel@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/transformer-babel@npm:2.8.3" + dependencies: + "@parcel/diagnostic": 2.8.3 + "@parcel/plugin": 2.8.3 + "@parcel/source-map": ^2.1.1 + "@parcel/utils": 2.8.3 + browserslist: ^4.6.6 + json5: ^2.2.0 + nullthrows: ^1.1.1 + semver: ^5.7.0 + checksum: a27bbe8d893854a77d9a8c9b45490728b2db81ad0782b7d9085d00c50155840477dd4ada8e382e0b02f9f5f8761da48bd6d3feb62ddd582e6608f92d4468df80 + languageName: node + linkType: hard + +"@parcel/transformer-css@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/transformer-css@npm:2.8.3" + dependencies: + "@parcel/diagnostic": 2.8.3 + "@parcel/plugin": 2.8.3 + "@parcel/source-map": ^2.1.1 + "@parcel/utils": 2.8.3 + browserslist: ^4.6.6 + lightningcss: ^1.16.1 + nullthrows: ^1.1.1 + checksum: 31375a140550968a36f7a8eb998c03f20200d202b7c62c98fb49b05f719777ca545d08f356dec9ca6d9a601ba0020abce5cf4672fe425bc99a540dccf262a6cc + languageName: node + linkType: hard + +"@parcel/transformer-html@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/transformer-html@npm:2.8.3" + dependencies: + "@parcel/diagnostic": 2.8.3 + "@parcel/hash": 2.8.3 + "@parcel/plugin": 2.8.3 + nullthrows: ^1.1.1 + posthtml: ^0.16.5 + posthtml-parser: ^0.10.1 + posthtml-render: ^3.0.0 + semver: ^5.7.1 + srcset: 4 + checksum: 21600a3e0ac9e05aa6f6066ef94f8ba7e0de62a8ae59a812230907f5731dcf73dc5308fb74b32bfb6dab16089d13f72043965e1e87e8c4daec8447a9081af8eb + languageName: node + linkType: hard + +"@parcel/transformer-image@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/transformer-image@npm:2.8.3" + dependencies: + "@parcel/plugin": 2.8.3 + "@parcel/utils": 2.8.3 + "@parcel/workers": 2.8.3 + nullthrows: ^1.1.1 + peerDependencies: + "@parcel/core": ^2.8.3 + checksum: f4b3464828e1b3d44e7da5c7a71272f5f53f830d9bb371e8dd8b2f32040f4426f3efeae12949947e34b39f7755a253f0b48c6eeec6d86ad43baf0b30717f1f47 + languageName: node + linkType: hard + +"@parcel/transformer-js@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/transformer-js@npm:2.8.3" + dependencies: + "@parcel/diagnostic": 2.8.3 + "@parcel/plugin": 2.8.3 + "@parcel/source-map": ^2.1.1 + "@parcel/utils": 2.8.3 + "@parcel/workers": 2.8.3 + "@swc/helpers": ^0.4.12 + browserslist: ^4.6.6 + detect-libc: ^1.0.3 + nullthrows: ^1.1.1 + regenerator-runtime: ^0.13.7 + semver: ^5.7.1 + peerDependencies: + "@parcel/core": ^2.8.3 + checksum: 29fb203502309e11452837e4ae60589300c0d91fae35cf4774e70959e9f4532960ef4619959ce9c95f0060020faabbcfd024b076f41c7d5f7e126c3547244ff6 + languageName: node + linkType: hard + +"@parcel/transformer-json@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/transformer-json@npm:2.8.3" + dependencies: + "@parcel/plugin": 2.8.3 + json5: ^2.2.0 + checksum: 04da28b0f0ff1ec1d7c6383b880daa2918f85ba1375351690a9a07ea4de102531d5f6addb3091ae5109623e270e1d2cdf582661f4a0805bd982a653a06d26890 + languageName: node + linkType: hard + +"@parcel/transformer-postcss@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/transformer-postcss@npm:2.8.3" + dependencies: + "@parcel/diagnostic": 2.8.3 + "@parcel/hash": 2.8.3 + "@parcel/plugin": 2.8.3 + "@parcel/utils": 2.8.3 + clone: ^2.1.1 + nullthrows: ^1.1.1 + postcss-value-parser: ^4.2.0 + semver: ^5.7.1 + checksum: 2c75cb5cec7112d12a28ac5cddc9f2e939f76e006929757804431b266e7541aae5df6ba8601727c33c7b53f0f971a6df5dfb4394fa0baf284bd2c6fc9b507650 + languageName: node + linkType: hard + +"@parcel/transformer-posthtml@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/transformer-posthtml@npm:2.8.3" + dependencies: + "@parcel/plugin": 2.8.3 + "@parcel/utils": 2.8.3 + nullthrows: ^1.1.1 + posthtml: ^0.16.5 + posthtml-parser: ^0.10.1 + posthtml-render: ^3.0.0 + semver: ^5.7.1 + checksum: 130c95782aebb2491f2d89685db573b3b85ed1f7d9862684db2ab9d11fe8148995185a4e144b818de06d596cf687c5bd57b6b8648d2856cf830a9674c2ec3237 + languageName: node + linkType: hard + +"@parcel/transformer-raw@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/transformer-raw@npm:2.8.3" + dependencies: + "@parcel/plugin": 2.8.3 + checksum: 371263bb526373c229aa3e730f2a1d6687bd6b771203d73237c04da3a3ada86c4fcf0b534d3fb366a7b3842df0cf98ae1e033602613cafd9f702f47a6568a83c + languageName: node + linkType: hard + +"@parcel/transformer-react-refresh-wrap@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/transformer-react-refresh-wrap@npm:2.8.3" + dependencies: + "@parcel/plugin": 2.8.3 + "@parcel/utils": 2.8.3 + react-refresh: ^0.9.0 + checksum: e9648e04b7f9b29f47ec7baedfba9cc36bbb7e44be6ad4b6b4433c20d1b5a3184a3043b712add16a5cc06300289305d5fa9ebb73c6dc926d04df7c52d9bc3316 + languageName: node + linkType: hard + +"@parcel/transformer-svg@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/transformer-svg@npm:2.8.3" + dependencies: + "@parcel/diagnostic": 2.8.3 + "@parcel/hash": 2.8.3 + "@parcel/plugin": 2.8.3 + nullthrows: ^1.1.1 + posthtml: ^0.16.5 + posthtml-parser: ^0.10.1 + posthtml-render: ^3.0.0 + semver: ^5.7.1 + checksum: 1f3db309e47d07849a2b4ffe11b508fd7ae792c0c0ce7b03e442fffb25f5e7425c5027428729bf2b587309265bba0be6da635d62c51ae8ab7e54483eff3f575e + languageName: node + linkType: hard + +"@parcel/types@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/types@npm:2.8.3" + dependencies: + "@parcel/cache": 2.8.3 + "@parcel/diagnostic": 2.8.3 + "@parcel/fs": 2.8.3 + "@parcel/package-manager": 2.8.3 + "@parcel/source-map": ^2.1.1 + "@parcel/workers": 2.8.3 + utility-types: ^3.10.0 + checksum: ece0abdd5c7cce32a246155f6828f6a92830341dfbceb81c9aaf7da44e0733b87ea8a607412dfe4b5ec59d7c9a3c1b1463b94ec8a5a82b745541881952003a16 + languageName: node + linkType: hard + +"@parcel/utils@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/utils@npm:2.8.3" + dependencies: + "@parcel/codeframe": 2.8.3 + "@parcel/diagnostic": 2.8.3 + "@parcel/hash": 2.8.3 + "@parcel/logger": 2.8.3 + "@parcel/markdown-ansi": 2.8.3 + "@parcel/source-map": ^2.1.1 + chalk: ^4.1.0 + checksum: 69edf3e7c3ef1ccd4caa6ca838a64b27b27668b213212579111405824ed969e6555857b33f0b9e793e97399a60f034904addde19b98628b37a2fcbbb9141cafa + languageName: node + linkType: hard + +"@parcel/watcher@npm:^2.0.7": + version: 2.1.0 + resolution: "@parcel/watcher@npm:2.1.0" + dependencies: + is-glob: ^4.0.3 + micromatch: ^4.0.5 + node-addon-api: ^3.2.1 + node-gyp: latest + node-gyp-build: ^4.3.0 + checksum: 17f512ad6d5dbb40053ceea7091f8af754afc63786b8f050b225b89a8ba24900468aad8bc4edb25c0349b4c0c8d061f50aa19242c0af52cbc30e6ebf50c7bf4c + languageName: node + linkType: hard + +"@parcel/workers@npm:2.8.3": + version: 2.8.3 + resolution: "@parcel/workers@npm:2.8.3" + dependencies: + "@parcel/diagnostic": 2.8.3 + "@parcel/logger": 2.8.3 + "@parcel/types": 2.8.3 + "@parcel/utils": 2.8.3 + chrome-trace-event: ^1.0.2 + nullthrows: ^1.1.1 + peerDependencies: + "@parcel/core": ^2.8.3 + checksum: e3168b3e9ee6bd8e92472e11af9228aca689c5d31841410c908ab31f2a11adf939481d9f4d945ae44d7d3ec1e07980fb3ca5c2f87be82e31a02a94f4655c8e01 + languageName: node + linkType: hard + +"@popperjs/core@npm:^2.6.0": + version: 2.11.7 + resolution: "@popperjs/core@npm:2.11.7" + checksum: 5b6553747899683452a1d28898c1b39173a4efd780e74360bfcda8eb42f1c5e819602769c81a10920fc68c881d07fb40429604517d499567eac079cfa6470f19 + languageName: node + linkType: hard + +"@swc/helpers@npm:^0.4.12": + version: 0.4.14 + resolution: "@swc/helpers@npm:0.4.14" + dependencies: + tslib: ^2.4.0 + checksum: 273fd3f3fc461a92f3790cc551ea054745c6d6959afbe1232e6d7aa1c722bbc114d308aab96bef5c78fc0303c85c7b472ef00e2253251cc89737f3b1af56e5a5 + languageName: node + linkType: hard + +"@tootallnate/once@npm:2": + version: 2.0.0 + resolution: "@tootallnate/once@npm:2.0.0" + checksum: ad87447820dd3f24825d2d947ebc03072b20a42bfc96cbafec16bff8bbda6c1a81fcb0be56d5b21968560c5359a0af4038a68ba150c3e1694fe4c109a063bed8 + languageName: node + linkType: hard + +"@trysound/sax@npm:0.2.0": + version: 0.2.0 + resolution: "@trysound/sax@npm:0.2.0" + checksum: 11226c39b52b391719a2a92e10183e4260d9651f86edced166da1d95f39a0a1eaa470e44d14ac685ccd6d3df7e2002433782872c0feeb260d61e80f21250e65c + languageName: node + linkType: hard + +"@types/parse-json@npm:^4.0.0": + version: 4.0.0 + resolution: "@types/parse-json@npm:4.0.0" + checksum: fd6bce2b674b6efc3db4c7c3d336bd70c90838e8439de639b909ce22f3720d21344f52427f1d9e57b265fcb7f6c018699b99e5e0c208a1a4823014269a6bf35b + languageName: node + linkType: hard + +"@types/prop-types@npm:*": + version: 15.7.5 + resolution: "@types/prop-types@npm:15.7.5" + checksum: 5b43b8b15415e1f298243165f1d44390403bb2bd42e662bca3b5b5633fdd39c938e91b7fce3a9483699db0f7a715d08cef220c121f723a634972fdf596aec980 + languageName: node + linkType: hard + +"@types/react-table@npm:^6.8.5": + version: 6.8.9 + resolution: "@types/react-table@npm:6.8.9" + dependencies: + "@types/react": "*" + checksum: 519333ed44fca2133d2349634f0e228292715e401ae3572cbef7429729110476f79338f8d817d7de1049895a9173951691430438f9cfac48ea49aad2cc8c66d9 + languageName: node + linkType: hard + +"@types/react@npm:*": + version: 18.0.29 + resolution: "@types/react@npm:18.0.29" + dependencies: + "@types/prop-types": "*" + "@types/scheduler": "*" + csstype: ^3.0.2 + checksum: 75f56f81d80a1c15ee3fcd2039bdc5f7a70f49cf69827ee0fc8beece9809b234f245dd62552e91018d8f25792986bd513b44caf054554d5c5eb979a5f9460214 + languageName: node + linkType: hard + +"@types/scheduler@npm:*": + version: 0.16.3 + resolution: "@types/scheduler@npm:0.16.3" + checksum: 2b0aec39c24268e3ce938c5db2f2e77f5c3dd280e05c262d9c2fe7d890929e4632a6b8e94334017b66b45e4f92a5aa42ba3356640c2a1175fa37bef2f5200767 + languageName: node + linkType: hard + +"abbrev@npm:^1.0.0": + version: 1.1.1 + resolution: "abbrev@npm:1.1.1" + checksum: a4a97ec07d7ea112c517036882b2ac22f3109b7b19077dc656316d07d308438aac28e4d9746dc4d84bf6b1e75b4a7b0a5f3cb30592419f128ca9a8cee3bcfa17 + languageName: node + linkType: hard + +"abortcontroller-polyfill@npm:^1.1.9": + version: 1.7.3 + resolution: "abortcontroller-polyfill@npm:1.7.3" + checksum: 55739d7f0c9bd6afa2aabb3148778967c4dd4dcff91f6b9259df38da34f9882d3f7730b0954e9767a19cc16a8dd9a58915da4e8a50220300d45af3817d7557b1 + languageName: node + linkType: hard + +"ace-builds@npm:^1.4.14": + version: 1.16.0 + resolution: "ace-builds@npm:1.16.0" + checksum: 1dca0aa438d6f1cb0204fafbdfb66500d8a68497a41b0566df74430b44b49595f6f8f892ef196383ab58f7ec6b3b513dbcd6f11bdad9e4170e84030a2e4a2ff4 + languageName: node + linkType: hard + +"acorn@npm:^8.5.0": + version: 8.7.1 + resolution: "acorn@npm:8.7.1" + bin: + acorn: bin/acorn + checksum: aca0aabf98826717920ac2583fdcad0a6fbe4e583fdb6e843af2594e907455aeafe30b1e14f1757cd83ce1776773cf8296ffc3a4acf13f0bd3dfebcf1db6ae80 + languageName: node + linkType: hard + +"agent-base@npm:6, agent-base@npm:^6.0.2": + version: 6.0.2 + resolution: "agent-base@npm:6.0.2" + dependencies: + debug: 4 + checksum: f52b6872cc96fd5f622071b71ef200e01c7c4c454ee68bc9accca90c98cfb39f2810e3e9aa330435835eedc8c23f4f8a15267f67c6e245d2b33757575bdac49d + languageName: node + linkType: hard + +"agentkeepalive@npm:^4.2.1": + version: 4.3.0 + resolution: "agentkeepalive@npm:4.3.0" + dependencies: + debug: ^4.1.0 + depd: ^2.0.0 + humanize-ms: ^1.2.1 + checksum: 982453aa44c11a06826c836025e5162c846e1200adb56f2d075400da7d32d87021b3b0a58768d949d824811f5654223d5a8a3dad120921a2439625eb847c6260 + languageName: node + linkType: hard + +"aggregate-error@npm:^3.0.0": + version: 3.1.0 + resolution: "aggregate-error@npm:3.1.0" + dependencies: + clean-stack: ^2.0.0 + indent-string: ^4.0.0 + checksum: 1101a33f21baa27a2fa8e04b698271e64616b886795fd43c31068c07533c7b3facfcaf4e9e0cab3624bd88f729a592f1c901a1a229c9e490eafce411a8644b79 + languageName: node + linkType: hard + +"ansi-regex@npm:^5.0.1": + version: 5.0.1 + resolution: "ansi-regex@npm:5.0.1" + checksum: 2aa4bb54caf2d622f1afdad09441695af2a83aa3fe8b8afa581d205e57ed4261c183c4d3877cee25794443fde5876417d859c108078ab788d6af7e4fe52eb66b + languageName: node + linkType: hard + +"ansi-styles@npm:^3.2.1": + version: 3.2.1 + resolution: "ansi-styles@npm:3.2.1" + dependencies: + color-convert: ^1.9.0 + checksum: d85ade01c10e5dd77b6c89f34ed7531da5830d2cb5882c645f330079975b716438cd7ebb81d0d6e6b4f9c577f19ae41ab55f07f19786b02f9dfd9e0377395665 + languageName: node + linkType: hard + +"ansi-styles@npm:^4.1.0": + version: 4.3.0 + resolution: "ansi-styles@npm:4.3.0" + dependencies: + color-convert: ^2.0.1 + checksum: 513b44c3b2105dd14cc42a19271e80f386466c4be574bccf60b627432f9198571ebf4ab1e4c3ba17347658f4ee1711c163d574248c0c1cdc2d5917a0ad582ec4 + languageName: node + linkType: hard + +"aproba@npm:^1.0.3 || ^2.0.0": + version: 2.0.0 + resolution: "aproba@npm:2.0.0" + checksum: 5615cadcfb45289eea63f8afd064ab656006361020e1735112e346593856f87435e02d8dcc7ff0d11928bc7d425f27bc7c2a84f6c0b35ab0ff659c814c138a24 + languageName: node + linkType: hard + +"are-we-there-yet@npm:^3.0.0": + version: 3.0.1 + resolution: "are-we-there-yet@npm:3.0.1" + dependencies: + delegates: ^1.0.0 + readable-stream: ^3.6.0 + checksum: 52590c24860fa7173bedeb69a4c05fb573473e860197f618b9a28432ee4379049336727ae3a1f9c4cb083114601c1140cee578376164d0e651217a9843f9fe83 + languageName: node + linkType: hard + +"babel-plugin-polyfill-corejs2@npm:^0.3.3": + version: 0.3.3 + resolution: "babel-plugin-polyfill-corejs2@npm:0.3.3" + dependencies: + "@babel/compat-data": ^7.17.7 + "@babel/helper-define-polyfill-provider": ^0.3.3 + semver: ^6.1.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 7db3044993f3dddb3cc3d407bc82e640964a3bfe22de05d90e1f8f7a5cb71460011ab136d3c03c6c1ba428359ebf635688cd6205e28d0469bba221985f5c6179 + languageName: node + linkType: hard + +"babel-plugin-polyfill-corejs3@npm:^0.6.0": + version: 0.6.0 + resolution: "babel-plugin-polyfill-corejs3@npm:0.6.0" + dependencies: + "@babel/helper-define-polyfill-provider": ^0.3.3 + core-js-compat: ^3.25.1 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 470bb8c59f7c0912bd77fe1b5a2e72f349b3f65bbdee1d60d6eb7e1f4a085c6f24b2dd5ab4ac6c2df6444a96b070ef6790eccc9edb6a2668c60d33133bfb62c6 + languageName: node + linkType: hard + +"babel-plugin-polyfill-regenerator@npm:^0.4.1": + version: 0.4.1 + resolution: "babel-plugin-polyfill-regenerator@npm:0.4.1" + dependencies: + "@babel/helper-define-polyfill-provider": ^0.3.3 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: ab0355efbad17d29492503230387679dfb780b63b25408990d2e4cf421012dae61d6199ddc309f4d2409ce4e9d3002d187702700dd8f4f8770ebbba651ed066c + languageName: node + linkType: hard + +"balanced-match@npm:^1.0.0": + version: 1.0.2 + resolution: "balanced-match@npm:1.0.2" + checksum: 9706c088a283058a8a99e0bf91b0a2f75497f185980d9ffa8b304de1d9e58ebda7c72c07ebf01dadedaac5b2907b2c6f566f660d62bd336c3468e960403b9d65 + languageName: node + linkType: hard + +"base-x@npm:^3.0.8": + version: 3.0.9 + resolution: "base-x@npm:3.0.9" + dependencies: + safe-buffer: ^5.0.1 + checksum: 957101d6fd09e1903e846fd8f69fd7e5e3e50254383e61ab667c725866bec54e5ece5ba49ce385128ae48f9ec93a26567d1d5ebb91f4d56ef4a9cc0d5a5481e8 + languageName: node + linkType: hard + +"boolbase@npm:^1.0.0": + version: 1.0.0 + resolution: "boolbase@npm:1.0.0" + checksum: 3e25c80ef626c3a3487c73dbfc70ac322ec830666c9ad915d11b701142fab25ec1e63eff2c450c74347acfd2de854ccde865cd79ef4db1683f7c7b046ea43bb0 + languageName: node + linkType: hard + +"bootstrap@npm:^5.2.3": + version: 5.2.3 + resolution: "bootstrap@npm:5.2.3" + peerDependencies: + "@popperjs/core": ^2.11.6 + checksum: 0211805dec6a190c0911d142966df30fdb4b4139a04cc6c23dd83c6045ea3cb0a966b360ab2e701e7b3ad96ff01e05fdc0914be97b41bd876b11e457a8bdc6a3 + languageName: node + linkType: hard + +"bootswatch@npm:^5.2.3": + version: 5.2.3 + resolution: "bootswatch@npm:5.2.3" + checksum: 4f0319a3326a9123fda250813fec42c99a87ad4eaf5dca980de277b6228dc44473223ce602646249ee5570cc63e61ecb24a1a9adfaec5c48a13a0998c7fde9e9 + languageName: node + linkType: hard + +"brace-expansion@npm:^1.1.7": + version: 1.1.11 + resolution: "brace-expansion@npm:1.1.11" + dependencies: + balanced-match: ^1.0.0 + concat-map: 0.0.1 + checksum: faf34a7bb0c3fcf4b59c7808bc5d2a96a40988addf2e7e09dfbb67a2251800e0d14cd2bfc1aa79174f2f5095c54ff27f46fb1289fe2d77dac755b5eb3434cc07 + languageName: node + linkType: hard + +"brace-expansion@npm:^2.0.1": + version: 2.0.1 + resolution: "brace-expansion@npm:2.0.1" + dependencies: + balanced-match: ^1.0.0 + checksum: a61e7cd2e8a8505e9f0036b3b6108ba5e926b4b55089eeb5550cd04a471fe216c96d4fe7e4c7f995c728c554ae20ddfc4244cad10aef255e72b62930afd233d1 + languageName: node + linkType: hard + +"braces@npm:^3.0.2": + version: 3.0.2 + resolution: "braces@npm:3.0.2" + dependencies: + fill-range: ^7.0.1 + checksum: e2a8e769a863f3d4ee887b5fe21f63193a891c68b612ddb4b68d82d1b5f3ff9073af066c343e9867a393fe4c2555dcb33e89b937195feb9c1613d259edfcd459 + languageName: node + linkType: hard + +"browserslist@npm:^4.21.3, browserslist@npm:^4.21.5": + version: 4.21.5 + resolution: "browserslist@npm:4.21.5" + dependencies: + caniuse-lite: ^1.0.30001449 + electron-to-chromium: ^1.4.284 + node-releases: ^2.0.8 + update-browserslist-db: ^1.0.10 + bin: + browserslist: cli.js + checksum: 9755986b22e73a6a1497fd8797aedd88e04270be33ce66ed5d85a1c8a798292a65e222b0f251bafa1c2522261e237d73b08b58689d4920a607e5a53d56dc4706 + languageName: node + linkType: hard + +"browserslist@npm:^4.6.6": + version: 4.20.4 + resolution: "browserslist@npm:4.20.4" + dependencies: + caniuse-lite: ^1.0.30001349 + electron-to-chromium: ^1.4.147 + escalade: ^3.1.1 + node-releases: ^2.0.5 + picocolors: ^1.0.0 + bin: + browserslist: cli.js + checksum: 0e56c42da765524e5c31bc9a1f08afaa8d5dba085071137cf21e56dc78d0cf0283764143df4c7d1c0cd18c3187fc9494e1d93fa0255004f0be493251a28635f9 + languageName: node + linkType: hard + +"buffer-from@npm:^1.0.0": + version: 1.1.1 + resolution: "buffer-from@npm:1.1.1" + checksum: ccc53b69736008bff764497367c4d24879ba7122bc619ee499ff47eef3a5b885ca496e87272e7ebffa0bec3804c83f84041c616f6e3318f40624e27c1d80f045 + languageName: node + linkType: hard + +"cacache@npm:^16.1.0": + version: 16.1.3 + resolution: "cacache@npm:16.1.3" + dependencies: + "@npmcli/fs": ^2.1.0 + "@npmcli/move-file": ^2.0.0 + chownr: ^2.0.0 + fs-minipass: ^2.1.0 + glob: ^8.0.1 + infer-owner: ^1.0.4 + lru-cache: ^7.7.1 + minipass: ^3.1.6 + minipass-collect: ^1.0.2 + minipass-flush: ^1.0.5 + minipass-pipeline: ^1.2.4 + mkdirp: ^1.0.4 + p-map: ^4.0.0 + promise-inflight: ^1.0.1 + rimraf: ^3.0.2 + ssri: ^9.0.0 + tar: ^6.1.11 + unique-filename: ^2.0.0 + checksum: d91409e6e57d7d9a3a25e5dcc589c84e75b178ae8ea7de05cbf6b783f77a5fae938f6e8fda6f5257ed70000be27a681e1e44829251bfffe4c10216002f8f14e6 + languageName: node + linkType: hard + +"callsites@npm:^3.0.0": + version: 3.1.0 + resolution: "callsites@npm:3.1.0" + checksum: 072d17b6abb459c2ba96598918b55868af677154bec7e73d222ef95a8fdb9bbf7dae96a8421085cdad8cd190d86653b5b6dc55a4484f2e5b2e27d5e0c3fc15b3 + languageName: node + linkType: hard + +"caniuse-lite@npm:^1.0.30001349, caniuse-lite@npm:^1.0.30001449": + version: 1.0.30001469 + resolution: "caniuse-lite@npm:1.0.30001469" + checksum: 8e496509d7e9ff189c72205675b5db0c5f1b6a09917027441e835efae0848a468a8c4e7d2b409ffc202438fcd23ae53e017f976a03c22c04d12d3c0e1e33e5de + languageName: node + linkType: hard + +"chalk@npm:^2.0.0": + version: 2.4.2 + resolution: "chalk@npm:2.4.2" + dependencies: + ansi-styles: ^3.2.1 + escape-string-regexp: ^1.0.5 + supports-color: ^5.3.0 + checksum: ec3661d38fe77f681200f878edbd9448821924e0f93a9cefc0e26a33b145f1027a2084bf19967160d11e1f03bfe4eaffcabf5493b89098b2782c3fe0b03d80c2 + languageName: node + linkType: hard + +"chalk@npm:^4.1.0": + version: 4.1.2 + resolution: "chalk@npm:4.1.2" + dependencies: + ansi-styles: ^4.1.0 + supports-color: ^7.1.0 + checksum: fe75c9d5c76a7a98d45495b91b2172fa3b7a09e0cc9370e5c8feb1c567b85c4288e2b3fded7cfdd7359ac28d6b3844feb8b82b8686842e93d23c827c417e83fc + languageName: node + linkType: hard + +"chownr@npm:^2.0.0": + version: 2.0.0 + resolution: "chownr@npm:2.0.0" + checksum: c57cf9dd0791e2f18a5ee9c1a299ae6e801ff58fee96dc8bfd0dcb4738a6ce58dd252a3605b1c93c6418fe4f9d5093b28ffbf4d66648cb2a9c67eaef9679be2f + languageName: node + linkType: hard + +"chrome-trace-event@npm:^1.0.2": + version: 1.0.3 + resolution: "chrome-trace-event@npm:1.0.3" + checksum: cb8b1fc7e881aaef973bd0c4a43cd353c2ad8323fb471a041e64f7c2dd849cde4aad15f8b753331a32dda45c973f032c8a03b8177fc85d60eaa75e91e08bfb97 + languageName: node + linkType: hard + +"classnames@npm:^2.2.3, classnames@npm:^2.2.5": + version: 2.2.6 + resolution: "classnames@npm:2.2.6" + checksum: 09a4fda780158aa8399079898eabeeca0c48c28641d9e4de140db7412e5e346843039ded1af0152f755afc2cc246ff8c3d6f227bf0dcb004e070b7fa14ec54cc + languageName: node + linkType: hard + +"classnames@npm:^2.3.2": + version: 2.3.2 + resolution: "classnames@npm:2.3.2" + checksum: 2c62199789618d95545c872787137262e741f9db13328e216b093eea91c85ef2bfb152c1f9e63027204e2559a006a92eb74147d46c800a9f96297ae1d9f96f4e + languageName: node + linkType: hard + +"clean-stack@npm:^2.0.0": + version: 2.2.0 + resolution: "clean-stack@npm:2.2.0" + checksum: 2ac8cd2b2f5ec986a3c743935ec85b07bc174d5421a5efc8017e1f146a1cf5f781ae962618f416352103b32c9cd7e203276e8c28241bbe946160cab16149fb68 + languageName: node + linkType: hard + +"clone@npm:^2.1.1": + version: 2.1.2 + resolution: "clone@npm:2.1.2" + checksum: aaf106e9bc025b21333e2f4c12da539b568db4925c0501a1bf4070836c9e848c892fa22c35548ce0d1132b08bbbfa17a00144fe58fccdab6fa900fec4250f67d + languageName: node + linkType: hard + +"color-convert@npm:^1.9.0": + version: 1.9.2 + resolution: "color-convert@npm:1.9.2" + dependencies: + color-name: 1.1.1 + checksum: a3844b83f3a3e5f5913b9f2f4f0233aedc1be02ee0ff6029039b2d6a4cd6941eb9c647c4c816d925fa1be387e029b94fc066df1202ef6cd74ab42325a86a2a7c + languageName: node + linkType: hard + +"color-convert@npm:^2.0.1": + version: 2.0.1 + resolution: "color-convert@npm:2.0.1" + dependencies: + color-name: ~1.1.4 + checksum: 79e6bdb9fd479a205c71d89574fccfb22bd9053bd98c6c4d870d65c132e5e904e6034978e55b43d69fcaa7433af2016ee203ce76eeba9cfa554b373e7f7db336 + languageName: node + linkType: hard + +"color-name@npm:1.1.1": + version: 1.1.1 + resolution: "color-name@npm:1.1.1" + checksum: 33da87fff7042ee320bb48db561f503dbf18e0b1b7ae8176bbe3ca518d5e74f4b5f19038c5a8526fa9e5361ba628ab98fde8fcfd05b8a6c2d54511f158b0a358 + languageName: node + linkType: hard + +"color-name@npm:~1.1.4": + version: 1.1.4 + resolution: "color-name@npm:1.1.4" + checksum: b0445859521eb4021cd0fb0cc1a75cecf67fceecae89b63f62b201cca8d345baf8b952c966862a9d9a2632987d4f6581f0ec8d957dfacece86f0a7919316f610 + languageName: node + linkType: hard + +"color-support@npm:^1.1.3": + version: 1.1.3 + resolution: "color-support@npm:1.1.3" + bin: + color-support: bin.js + checksum: 9b7356817670b9a13a26ca5af1c21615463b500783b739b7634a0c2047c16cef4b2865d7576875c31c3cddf9dd621fa19285e628f20198b233a5cfdda6d0793b + languageName: node + linkType: hard + +"commander@npm:^2.20.0": + version: 2.20.3 + resolution: "commander@npm:2.20.3" + checksum: ab8c07884e42c3a8dbc5dd9592c606176c7eb5c1ca5ff274bcf907039b2c41de3626f684ea75ccf4d361ba004bbaff1f577d5384c155f3871e456bdf27becf9e + languageName: node + linkType: hard + +"commander@npm:^7.0.0, commander@npm:^7.2.0": + version: 7.2.0 + resolution: "commander@npm:7.2.0" + checksum: 53501cbeee61d5157546c0bef0fedb6cdfc763a882136284bed9a07225f09a14b82d2a84e7637edfd1a679fb35ed9502fd58ef1d091e6287f60d790147f68ddc + languageName: node + linkType: hard + +"concat-map@npm:0.0.1": + version: 0.0.1 + resolution: "concat-map@npm:0.0.1" + checksum: 902a9f5d8967a3e2faf138d5cb784b9979bad2e6db5357c5b21c568df4ebe62bcb15108af1b2253744844eb964fc023fbd9afbbbb6ddd0bcc204c6fb5b7bf3af + languageName: node + linkType: hard + +"console-control-strings@npm:^1.1.0": + version: 1.1.0 + resolution: "console-control-strings@npm:1.1.0" + checksum: 8755d76787f94e6cf79ce4666f0c5519906d7f5b02d4b884cf41e11dcd759ed69c57da0670afd9236d229a46e0f9cf519db0cd829c6dca820bb5a5c3def584ed + languageName: node + linkType: hard + +"convert-source-map@npm:^1.7.0": + version: 1.8.0 + resolution: "convert-source-map@npm:1.8.0" + dependencies: + safe-buffer: ~5.1.1 + checksum: 985d974a2d33e1a2543ada51c93e1ba2f73eaed608dc39f229afc78f71dcc4c8b7d7c684aa647e3c6a3a204027444d69e53e169ce94e8d1fa8d7dee80c9c8fed + languageName: node + linkType: hard + +"core-js-compat@npm:^3.25.1": + version: 3.29.1 + resolution: "core-js-compat@npm:3.29.1" + dependencies: + browserslist: ^4.21.5 + checksum: 7260f6bbaa98836cda09a3b61aa721149d3ae95040302fb3b27eb153ae9bbddc8dee5249e72004cdc9552532029de4d50a5b2b066c37414421d2929d6091b18f + languageName: node + linkType: hard + +"core-js@npm:^3.1.3": + version: 3.29.1 + resolution: "core-js@npm:3.29.1" + checksum: b38446dbfcfd3887b3d4922990da487e2c95044cb4c5717aaf95e786a4c6b218f05c056c7ed6c699169b9794a49fec890e402659d54661fc56965a0eb717e7bd + languageName: node + linkType: hard + +"cosmiconfig@npm:^7.0.1": + version: 7.0.1 + resolution: "cosmiconfig@npm:7.0.1" + dependencies: + "@types/parse-json": ^4.0.0 + import-fresh: ^3.2.1 + parse-json: ^5.0.0 + path-type: ^4.0.0 + yaml: ^1.10.0 + checksum: 4be63e7117955fd88333d7460e4c466a90f556df6ef34efd59034d2463484e339666c41f02b523d574a797ec61f4a91918c5b89a316db2ea2f834e0d2d09465b + languageName: node + linkType: hard + +"css-select@npm:^4.1.3": + version: 4.3.0 + resolution: "css-select@npm:4.3.0" + dependencies: + boolbase: ^1.0.0 + css-what: ^6.0.1 + domhandler: ^4.3.1 + domutils: ^2.8.0 + nth-check: ^2.0.1 + checksum: d6202736839194dd7f910320032e7cfc40372f025e4bf21ca5bf6eb0a33264f322f50ba9c0adc35dadd342d3d6fae5ca244779a4873afbfa76561e343f2058e0 + languageName: node + linkType: hard + +"css-tree@npm:^1.1.2, css-tree@npm:^1.1.3": + version: 1.1.3 + resolution: "css-tree@npm:1.1.3" + dependencies: + mdn-data: 2.0.14 + source-map: ^0.6.1 + checksum: 79f9b81803991b6977b7fcb1588799270438274d89066ce08f117f5cdb5e20019b446d766c61506dd772c839df84caa16042d6076f20c97187f5abe3b50e7d1f + languageName: node + linkType: hard + +"css-what@npm:^6.0.1": + version: 6.1.0 + resolution: "css-what@npm:6.1.0" + checksum: b975e547e1e90b79625918f84e67db5d33d896e6de846c9b584094e529f0c63e2ab85ee33b9daffd05bff3a146a1916bec664e18bb76dd5f66cbff9fc13b2bbe + languageName: node + linkType: hard + +"csso@npm:^4.2.0": + version: 4.2.0 + resolution: "csso@npm:4.2.0" + dependencies: + css-tree: ^1.1.2 + checksum: 380ba9663da3bcea58dee358a0d8c4468bb6539be3c439dc266ac41c047217f52fd698fb7e4b6b6ccdfb8cf53ef4ceed8cc8ceccb8dfca2aa628319826b5b998 + languageName: node + linkType: hard + +"csstype@npm:^3.0.2": + version: 3.1.1 + resolution: "csstype@npm:3.1.1" + checksum: 1f7b4f5fdd955b7444b18ebdddf3f5c699159f13e9cf8ac9027ae4a60ae226aef9bbb14a6e12ca7dba3358b007cee6354b116e720262867c398de6c955ea451d + languageName: node + linkType: hard + +"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.3": + version: 4.3.4 + resolution: "debug@npm:4.3.4" + dependencies: + ms: 2.1.2 + peerDependenciesMeta: + supports-color: + optional: true + checksum: 3dbad3f94ea64f34431a9cbf0bafb61853eda57bff2880036153438f50fb5a84f27683ba0d8e5426bf41a8c6ff03879488120cf5b3a761e77953169c0600a708 + languageName: node + linkType: hard + +"delegates@npm:^1.0.0": + version: 1.0.0 + resolution: "delegates@npm:1.0.0" + checksum: a51744d9b53c164ba9c0492471a1a2ffa0b6727451bdc89e31627fdf4adda9d51277cfcbfb20f0a6f08ccb3c436f341df3e92631a3440226d93a8971724771fd + languageName: node + linkType: hard + +"depd@npm:^2.0.0": + version: 2.0.0 + resolution: "depd@npm:2.0.0" + checksum: abbe19c768c97ee2eed6282d8ce3031126662252c58d711f646921c9623f9052e3e1906443066beec1095832f534e57c523b7333f8e7e0d93051ab6baef5ab3a + languageName: node + linkType: hard + +"detect-libc@npm:^1.0.3": + version: 1.0.3 + resolution: "detect-libc@npm:1.0.3" + bin: + detect-libc: ./bin/detect-libc.js + checksum: daaaed925ffa7889bd91d56e9624e6c8033911bb60f3a50a74a87500680652969dbaab9526d1e200a4c94acf80fc862a22131841145a0a8482d60a99c24f4a3e + languageName: node + linkType: hard + +"diff-match-patch@npm:^1.0.5": + version: 1.0.5 + resolution: "diff-match-patch@npm:1.0.5" + checksum: 841522d01b09cccbc4e4402cf61514a81b906349a7d97b67222390f2d35cf5df277cb23959eeed212d5e46afb5629cebab41b87918672c5a05c11c73688630e3 + languageName: node + linkType: hard + +"dom-helpers@npm:^5.0.1": + version: 5.2.1 + resolution: "dom-helpers@npm:5.2.1" + dependencies: + "@babel/runtime": ^7.8.7 + csstype: ^3.0.2 + checksum: 863ba9e086f7093df3376b43e74ce4422571d404fc9828bf2c56140963d5edf0e56160f9b2f3bb61b282c07f8fc8134f023c98fd684bddcb12daf7b0f14d951c + languageName: node + linkType: hard + +"dom-serializer@npm:^1.0.1": + version: 1.4.1 + resolution: "dom-serializer@npm:1.4.1" + dependencies: + domelementtype: ^2.0.1 + domhandler: ^4.2.0 + entities: ^2.0.0 + checksum: fbb0b01f87a8a2d18e6e5a388ad0f7ec4a5c05c06d219377da1abc7bb0f674d804f4a8a94e3f71ff15f6cb7dcfc75704a54b261db672b9b3ab03da6b758b0b22 + languageName: node + linkType: hard + +"domelementtype@npm:^2.0.1, domelementtype@npm:^2.2.0": + version: 2.3.0 + resolution: "domelementtype@npm:2.3.0" + checksum: ee837a318ff702622f383409d1f5b25dd1024b692ef64d3096ff702e26339f8e345820f29a68bcdcea8cfee3531776b3382651232fbeae95612d6f0a75efb4f6 + languageName: node + linkType: hard + +"domhandler@npm:^4.2.0, domhandler@npm:^4.2.2, domhandler@npm:^4.3.1": + version: 4.3.1 + resolution: "domhandler@npm:4.3.1" + dependencies: + domelementtype: ^2.2.0 + checksum: 4c665ceed016e1911bf7d1dadc09dc888090b64dee7851cccd2fcf5442747ec39c647bb1cb8c8919f8bbdd0f0c625a6bafeeed4b2d656bbecdbae893f43ffaaa + languageName: node + linkType: hard + +"domutils@npm:^2.8.0": + version: 2.8.0 + resolution: "domutils@npm:2.8.0" + dependencies: + dom-serializer: ^1.0.1 + domelementtype: ^2.2.0 + domhandler: ^4.2.0 + checksum: abf7434315283e9aadc2a24bac0e00eab07ae4313b40cc239f89d84d7315ebdfd2fb1b5bf750a96bc1b4403d7237c7b2ebf60459be394d625ead4ca89b934391 + languageName: node + linkType: hard + +"dotenv-expand@npm:^5.1.0": + version: 5.1.0 + resolution: "dotenv-expand@npm:5.1.0" + checksum: 8017675b7f254384915d55f9eb6388e577cf0a1231a28d54b0ca03b782be9501b0ac90ac57338636d395fa59051e6209e9b44b8ddf169ce6076dffb5dea227d3 + languageName: node + linkType: hard + +"dotenv@npm:^7.0.0": + version: 7.0.0 + resolution: "dotenv@npm:7.0.0" + checksum: 18a7b3ef0e90fd6fcce7c7cbdd48d923b0cb180807540b80c797bda4a098097e17820d6315ae28eec22f73954cd0ab9d81904d46370183817c09f694d40566ff + languageName: node + linkType: hard + +"dygraphs@npm:^2.2.1": + version: 2.2.1 + resolution: "dygraphs@npm:2.2.1" + checksum: 871d4c2fdf441d8cb819ddca7cd2aa4321587d625775fe85560b902c7d443412503393100e1375489f3ecd36ae4e90fbe1b1e6d68adb04952cd9f8ff3201354e + languageName: node + linkType: hard + +"electron-to-chromium@npm:^1.4.147": + version: 1.4.154 + resolution: "electron-to-chromium@npm:1.4.154" + checksum: e04c6f8eb630a0ae9c62a9d00272fb154b5f5576bcd5f71cc4f8eb288603099a19b1544facd462f11bd3c52418d51b32de6d1c03050c8aadfcec995adc9ef8e6 + languageName: node + linkType: hard + +"electron-to-chromium@npm:^1.4.284": + version: 1.4.340 + resolution: "electron-to-chromium@npm:1.4.340" + checksum: b7ffa64814efa49891c23ca0560f6831a992fa904c463bbf635ceccd57821dd0f4ecc22fa066860d34d9614b0ef4b27b385a5f59aed5dcf73ed832d58f746780 + languageName: node + linkType: hard + +"emoji-regex@npm:^8.0.0": + version: 8.0.0 + resolution: "emoji-regex@npm:8.0.0" + checksum: d4c5c39d5a9868b5fa152f00cada8a936868fd3367f33f71be515ecee4c803132d11b31a6222b2571b1e5f7e13890156a94880345594d0ce7e3c9895f560f192 + languageName: node + linkType: hard + +"encoding@npm:^0.1.13": + version: 0.1.13 + resolution: "encoding@npm:0.1.13" + dependencies: + iconv-lite: ^0.6.2 + checksum: bb98632f8ffa823996e508ce6a58ffcf5856330fde839ae42c9e1f436cc3b5cc651d4aeae72222916545428e54fd0f6aa8862fd8d25bdbcc4589f1e3f3715e7f + languageName: node + linkType: hard + +"entities@npm:^2.0.0": + version: 2.2.0 + resolution: "entities@npm:2.2.0" + checksum: 19010dacaf0912c895ea262b4f6128574f9ccf8d4b3b65c7e8334ad0079b3706376360e28d8843ff50a78aabcb8f08f0a32dbfacdc77e47ed77ca08b713669b3 + languageName: node + linkType: hard + +"entities@npm:^3.0.1": + version: 3.0.1 + resolution: "entities@npm:3.0.1" + checksum: aaf7f12033f0939be91f5161593f853f2da55866db55ccbf72f45430b8977e2b79dbd58c53d0fdd2d00bd7d313b75b0968d09f038df88e308aa97e39f9456572 + languageName: node + linkType: hard + +"env-paths@npm:^2.2.0": + version: 2.2.1 + resolution: "env-paths@npm:2.2.1" + checksum: 65b5df55a8bab92229ab2b40dad3b387fad24613263d103a97f91c9fe43ceb21965cd3392b1ccb5d77088021e525c4e0481adb309625d0cb94ade1d1fb8dc17e + languageName: node + linkType: hard + +"err-code@npm:^2.0.2": + version: 2.0.3 + resolution: "err-code@npm:2.0.3" + checksum: 8b7b1be20d2de12d2255c0bc2ca638b7af5171142693299416e6a9339bd7d88fc8d7707d913d78e0993176005405a236b066b45666b27b797252c771156ace54 + languageName: node + linkType: hard + +"error-ex@npm:^1.3.1": + version: 1.3.2 + resolution: "error-ex@npm:1.3.2" + dependencies: + is-arrayish: ^0.2.1 + checksum: c1c2b8b65f9c91b0f9d75f0debaa7ec5b35c266c2cac5de412c1a6de86d4cbae04ae44e510378cb14d032d0645a36925d0186f8bb7367bcc629db256b743a001 + languageName: node + linkType: hard + +"escalade@npm:^3.1.1": + version: 3.1.1 + resolution: "escalade@npm:3.1.1" + checksum: a3e2a99f07acb74b3ad4989c48ca0c3140f69f923e56d0cba0526240ee470b91010f9d39001f2a4a313841d237ede70a729e92125191ba5d21e74b106800b133 + languageName: node + linkType: hard + +"escape-string-regexp@npm:^1.0.5": + version: 1.0.5 + resolution: "escape-string-regexp@npm:1.0.5" + checksum: 6092fda75c63b110c706b6a9bfde8a612ad595b628f0bd2147eea1d3406723020810e591effc7db1da91d80a71a737a313567c5abb3813e8d9c71f4aa595b410 + languageName: node + linkType: hard + +"esutils@npm:^2.0.2": + version: 2.0.2 + resolution: "esutils@npm:2.0.2" + checksum: d1ad79417f2ad62a7e37c01a6a2767c6d69d991976ed5b0e5ea446dbb758be58a60a892f388db036333b9815a829117a9eb4c881954f9baca2f65c4add3beeb8 + languageName: node + linkType: hard + +"feather-icons@npm:^4.29.0": + version: 4.29.0 + resolution: "feather-icons@npm:4.29.0" + dependencies: + classnames: ^2.2.5 + core-js: ^3.1.3 + checksum: 13f344736de25e7235fe9ab67c00c88e30396c0ff416614cbfeedf0159cac64890b04d2c704b005f2b1fab8ba79a8fd22e96ed2b30fbb3f9a41de0f491da8993 + languageName: node + linkType: hard + +"fill-range@npm:^7.0.1": + version: 7.0.1 + resolution: "fill-range@npm:7.0.1" + dependencies: + to-regex-range: ^5.0.1 + checksum: cc283f4e65b504259e64fd969bcf4def4eb08d85565e906b7d36516e87819db52029a76b6363d0f02d0d532f0033c9603b9e2d943d56ee3b0d4f7ad3328ff917 + languageName: node + linkType: hard + +"font-awesome@npm:^4.7.0": + version: 4.7.0 + resolution: "font-awesome@npm:4.7.0" + checksum: fa223f6e3b27e97d2d09cdbf0c1363e2ad18d2a685fc045f54e86394db59f7c113482a819de3b6489f42a630a8ec5911b8e65718e45f7cace1c0a1b05d7fce08 + languageName: node + linkType: hard + +"fs-minipass@npm:^2.0.0, fs-minipass@npm:^2.1.0": + version: 2.1.0 + resolution: "fs-minipass@npm:2.1.0" + dependencies: + minipass: ^3.0.0 + checksum: 1b8d128dae2ac6cc94230cc5ead341ba3e0efaef82dab46a33d171c044caaa6ca001364178d42069b2809c35a1c3c35079a32107c770e9ffab3901b59af8c8b1 + languageName: node + linkType: hard + +"fs.realpath@npm:^1.0.0": + version: 1.0.0 + resolution: "fs.realpath@npm:1.0.0" + checksum: 99ddea01a7e75aa276c250a04eedeffe5662bce66c65c07164ad6264f9de18fb21be9433ead460e54cff20e31721c811f4fb5d70591799df5f85dce6d6746fd0 + languageName: node + linkType: hard + +"function-bind@npm:^1.1.1": + version: 1.1.1 + resolution: "function-bind@npm:1.1.1" + checksum: b32fbaebb3f8ec4969f033073b43f5c8befbb58f1a79e12f1d7490358150359ebd92f49e72ff0144f65f2c48ea2a605bff2d07965f548f6474fd8efd95bf361a + languageName: node + linkType: hard + +"gauge@npm:^4.0.3": + version: 4.0.4 + resolution: "gauge@npm:4.0.4" + dependencies: + aproba: ^1.0.3 || ^2.0.0 + color-support: ^1.1.3 + console-control-strings: ^1.1.0 + has-unicode: ^2.0.1 + signal-exit: ^3.0.7 + string-width: ^4.2.3 + strip-ansi: ^6.0.1 + wide-align: ^1.1.5 + checksum: 788b6bfe52f1dd8e263cda800c26ac0ca2ff6de0b6eee2fe0d9e3abf15e149b651bd27bf5226be10e6e3edb5c4e5d5985a5a1a98137e7a892f75eff76467ad2d + languageName: node + linkType: hard + +"gensync@npm:^1.0.0-beta.2": + version: 1.0.0-beta.2 + resolution: "gensync@npm:1.0.0-beta.2" + checksum: a7437e58c6be12aa6c90f7730eac7fa9833dc78872b4ad2963d2031b00a3367a93f98aec75f9aaac7220848e4026d67a8655e870b24f20a543d103c0d65952ec + languageName: node + linkType: hard + +"get-port@npm:^4.2.0": + version: 4.2.0 + resolution: "get-port@npm:4.2.0" + checksum: 6c9a452b2d6e81fe36781a69ed201883d37c02f141ba5770eaef3eca768ca38777c2eba4bec303f6b8c3f45f29036f95d5606b255f613320a6b4b680e1975c07 + languageName: node + linkType: hard + +"glob@npm:^7.1.3, glob@npm:^7.1.4": + version: 7.2.3 + resolution: "glob@npm:7.2.3" + dependencies: + fs.realpath: ^1.0.0 + inflight: ^1.0.4 + inherits: 2 + minimatch: ^3.1.1 + once: ^1.3.0 + path-is-absolute: ^1.0.0 + checksum: 29452e97b38fa704dabb1d1045350fb2467cf0277e155aa9ff7077e90ad81d1ea9d53d3ee63bd37c05b09a065e90f16aec4a65f5b8de401d1dac40bc5605d133 + languageName: node + linkType: hard + +"glob@npm:^8.0.1": + version: 8.1.0 + resolution: "glob@npm:8.1.0" + dependencies: + fs.realpath: ^1.0.0 + inflight: ^1.0.4 + inherits: 2 + minimatch: ^5.0.1 + once: ^1.3.0 + checksum: 92fbea3221a7d12075f26f0227abac435de868dd0736a17170663783296d0dd8d3d532a5672b4488a439bf5d7fb85cdd07c11185d6cd39184f0385cbdfb86a47 + languageName: node + linkType: hard + +"globals@npm:^11.1.0": + version: 11.12.0 + resolution: "globals@npm:11.12.0" + checksum: 67051a45eca3db904aee189dfc7cd53c20c7d881679c93f6146ddd4c9f4ab2268e68a919df740d39c71f4445d2b38ee360fc234428baea1dbdfe68bbcb46979e + languageName: node + linkType: hard + +"globals@npm:^13.2.0": + version: 13.15.0 + resolution: "globals@npm:13.15.0" + dependencies: + type-fest: ^0.20.2 + checksum: 383ade0873b2ab29ce6d143466c203ed960491575bc97406395e5c8434026fb02472ab2dfff5bc16689b8460269b18fda1047975295cd0183904385c51258bae + languageName: node + linkType: hard + +"graceful-fs@npm:^4.2.6": + version: 4.2.11 + resolution: "graceful-fs@npm:4.2.11" + checksum: ac85f94da92d8eb6b7f5a8b20ce65e43d66761c55ce85ac96df6865308390da45a8d3f0296dd3a663de65d30ba497bd46c696cc1e248c72b13d6d567138a4fc7 + languageName: node + linkType: hard + +"has-flag@npm:^3.0.0": + version: 3.0.0 + resolution: "has-flag@npm:3.0.0" + checksum: 4a15638b454bf086c8148979aae044dd6e39d63904cd452d970374fa6a87623423da485dfb814e7be882e05c096a7ccf1ebd48e7e7501d0208d8384ff4dea73b + languageName: node + linkType: hard + +"has-flag@npm:^4.0.0": + version: 4.0.0 + resolution: "has-flag@npm:4.0.0" + checksum: 261a1357037ead75e338156b1f9452c016a37dcd3283a972a30d9e4a87441ba372c8b81f818cd0fbcd9c0354b4ae7e18b9e1afa1971164aef6d18c2b6095a8ad + languageName: node + linkType: hard + +"has-unicode@npm:^2.0.1": + version: 2.0.1 + resolution: "has-unicode@npm:2.0.1" + checksum: 1eab07a7436512db0be40a710b29b5dc21fa04880b7f63c9980b706683127e3c1b57cb80ea96d47991bdae2dfe479604f6a1ba410106ee1046a41d1bd0814400 + languageName: node + linkType: hard + +"has@npm:^1.0.3": + version: 1.0.3 + resolution: "has@npm:1.0.3" + dependencies: + function-bind: ^1.1.1 + checksum: b9ad53d53be4af90ce5d1c38331e712522417d017d5ef1ebd0507e07c2fbad8686fffb8e12ddecd4c39ca9b9b47431afbb975b8abf7f3c3b82c98e9aad052792 + languageName: node + linkType: hard + +"history@npm:^4.9.0": + version: 4.10.1 + resolution: "history@npm:4.10.1" + dependencies: + "@babel/runtime": ^7.1.2 + loose-envify: ^1.2.0 + resolve-pathname: ^3.0.0 + tiny-invariant: ^1.0.2 + tiny-warning: ^1.0.0 + value-equal: ^1.0.1 + checksum: addd84bc4683929bae4400419b5af132ff4e4e9b311a0d4e224579ea8e184a6b80d7f72c55927e4fa117f69076a9e47ce082d8d0b422f1a9ddac7991490ca1d0 + languageName: node + linkType: hard + +"hoist-non-react-statics@npm:^3.1.0": + version: 3.3.2 + resolution: "hoist-non-react-statics@npm:3.3.2" + dependencies: + react-is: ^16.7.0 + checksum: b1538270429b13901ee586aa44f4cc3ecd8831c061d06cb8322e50ea17b3f5ce4d0e2e66394761e6c8e152cd8c34fb3b4b690116c6ce2bd45b18c746516cb9e8 + languageName: node + linkType: hard + +"htmlnano@npm:^2.0.0": + version: 2.0.2 + resolution: "htmlnano@npm:2.0.2" + dependencies: + cosmiconfig: ^7.0.1 + posthtml: ^0.16.5 + timsort: ^0.3.0 + peerDependencies: + cssnano: ^5.0.11 + postcss: ^8.3.11 + purgecss: ^4.0.3 + relateurl: ^0.2.7 + srcset: ^5.0.0 + svgo: ^2.8.0 + terser: ^5.10.0 + uncss: ^0.17.3 + peerDependenciesMeta: + cssnano: + optional: true + postcss: + optional: true + purgecss: + optional: true + relateurl: + optional: true + srcset: + optional: true + svgo: + optional: true + terser: + optional: true + uncss: + optional: true + checksum: 41f9e0c0e54367730109e9ea31a1e625ebfa4134f6689d36aba76551cb62a9a5c200bee556b4ad12c230d3586243ac6ebaaaab93bb3091d7f96686a98c5caa1a + languageName: node + linkType: hard + +"htmlparser2@npm:^7.1.1": + version: 7.2.0 + resolution: "htmlparser2@npm:7.2.0" + dependencies: + domelementtype: ^2.0.1 + domhandler: ^4.2.2 + domutils: ^2.8.0 + entities: ^3.0.1 + checksum: 96563d9965729cfcb3f5f19c26d013c6831b4cb38d79d8c185e9cd669ea6a9ffe8fb9ccc74d29a068c9078aa0e2767053ed6b19aa32723c41550340d0094bea0 + languageName: node + linkType: hard + +"http-cache-semantics@npm:^4.1.0": + version: 4.1.1 + resolution: "http-cache-semantics@npm:4.1.1" + checksum: 83ac0bc60b17a3a36f9953e7be55e5c8f41acc61b22583060e8dedc9dd5e3607c823a88d0926f9150e571f90946835c7fe150732801010845c72cd8bbff1a236 + languageName: node + linkType: hard + +"http-proxy-agent@npm:^5.0.0": + version: 5.0.0 + resolution: "http-proxy-agent@npm:5.0.0" + dependencies: + "@tootallnate/once": 2 + agent-base: 6 + debug: 4 + checksum: e2ee1ff1656a131953839b2a19cd1f3a52d97c25ba87bd2559af6ae87114abf60971e498021f9b73f9fd78aea8876d1fb0d4656aac8a03c6caa9fc175f22b786 + languageName: node + linkType: hard + +"https-proxy-agent@npm:^5.0.0": + version: 5.0.1 + resolution: "https-proxy-agent@npm:5.0.1" + dependencies: + agent-base: 6 + debug: 4 + checksum: 571fccdf38184f05943e12d37d6ce38197becdd69e58d03f43637f7fa1269cf303a7d228aa27e5b27bbd3af8f09fd938e1c91dcfefff2df7ba77c20ed8dfc765 + languageName: node + linkType: hard + +"humanize-ms@npm:^1.2.1": + version: 1.2.1 + resolution: "humanize-ms@npm:1.2.1" + dependencies: + ms: ^2.0.0 + checksum: 9c7a74a2827f9294c009266c82031030eae811ca87b0da3dceb8d6071b9bde22c9f3daef0469c3c533cc67a97d8a167cd9fc0389350e5f415f61a79b171ded16 + languageName: node + linkType: hard + +"iconv-lite@npm:^0.6.2": + version: 0.6.3 + resolution: "iconv-lite@npm:0.6.3" + dependencies: + safer-buffer: ">= 2.1.2 < 3.0.0" + checksum: 3f60d47a5c8fc3313317edfd29a00a692cc87a19cac0159e2ce711d0ebc9019064108323b5e493625e25594f11c6236647d8e256fbe7a58f4a3b33b89e6d30bf + languageName: node + linkType: hard + +"import-fresh@npm:^3.2.1": + version: 3.3.0 + resolution: "import-fresh@npm:3.3.0" + dependencies: + parent-module: ^1.0.0 + resolve-from: ^4.0.0 + checksum: 2cacfad06e652b1edc50be650f7ec3be08c5e5a6f6d12d035c440a42a8cc028e60a5b99ca08a77ab4d6b1346da7d971915828f33cdab730d3d42f08242d09baa + languageName: node + linkType: hard + +"imurmurhash@npm:^0.1.4": + version: 0.1.4 + resolution: "imurmurhash@npm:0.1.4" + checksum: 7cae75c8cd9a50f57dadd77482359f659eaebac0319dd9368bcd1714f55e65badd6929ca58569da2b6494ef13fdd5598cd700b1eba23f8b79c5f19d195a3ecf7 + languageName: node + linkType: hard + +"indent-string@npm:^4.0.0": + version: 4.0.0 + resolution: "indent-string@npm:4.0.0" + checksum: 824cfb9929d031dabf059bebfe08cf3137365e112019086ed3dcff6a0a7b698cb80cf67ccccde0e25b9e2d7527aa6cc1fed1ac490c752162496caba3e6699612 + languageName: node + linkType: hard + +"infer-owner@npm:^1.0.4": + version: 1.0.4 + resolution: "infer-owner@npm:1.0.4" + checksum: 181e732764e4a0611576466b4b87dac338972b839920b2a8cde43642e4ed6bd54dc1fb0b40874728f2a2df9a1b097b8ff83b56d5f8f8e3927f837fdcb47d8a89 + languageName: node + linkType: hard + +"inflight@npm:^1.0.4": + version: 1.0.6 + resolution: "inflight@npm:1.0.6" + dependencies: + once: ^1.3.0 + wrappy: 1 + checksum: f4f76aa072ce19fae87ce1ef7d221e709afb59d445e05d47fba710e85470923a75de35bfae47da6de1b18afc3ce83d70facf44cfb0aff89f0a3f45c0a0244dfd + languageName: node + linkType: hard + +"inherits@npm:2, inherits@npm:^2.0.3": + version: 2.0.4 + resolution: "inherits@npm:2.0.4" + checksum: 4a48a733847879d6cf6691860a6b1e3f0f4754176e4d71494c41f3475553768b10f84b5ce1d40fbd0e34e6bfbb864ee35858ad4dd2cf31e02fc4a154b724d7f1 + languageName: node + linkType: hard + +"ip@npm:^2.0.0": + version: 2.0.0 + resolution: "ip@npm:2.0.0" + checksum: cfcfac6b873b701996d71ec82a7dd27ba92450afdb421e356f44044ed688df04567344c36cbacea7d01b1c39a4c732dc012570ebe9bebfb06f27314bca625349 + languageName: node + linkType: hard + +"is-arrayish@npm:^0.2.1": + version: 0.2.1 + resolution: "is-arrayish@npm:0.2.1" + checksum: eef4417e3c10e60e2c810b6084942b3ead455af16c4509959a27e490e7aee87cfb3f38e01bbde92220b528a0ee1a18d52b787e1458ee86174d8c7f0e58cd488f + languageName: node + linkType: hard + +"is-core-module@npm:^2.8.1": + version: 2.9.0 + resolution: "is-core-module@npm:2.9.0" + dependencies: + has: ^1.0.3 + checksum: b27034318b4b462f1c8f1dfb1b32baecd651d891a4e2d1922135daeff4141dfced2b82b07aef83ef54275c4a3526aa38da859223664d0868ca24182badb784ce + languageName: node + linkType: hard + +"is-extglob@npm:^2.1.1": + version: 2.1.1 + resolution: "is-extglob@npm:2.1.1" + checksum: df033653d06d0eb567461e58a7a8c9f940bd8c22274b94bf7671ab36df5719791aae15eef6d83bbb5e23283967f2f984b8914559d4449efda578c775c4be6f85 + languageName: node + linkType: hard + +"is-fullwidth-code-point@npm:^3.0.0": + version: 3.0.0 + resolution: "is-fullwidth-code-point@npm:3.0.0" + checksum: 44a30c29457c7fb8f00297bce733f0a64cd22eca270f83e58c105e0d015e45c019491a4ab2faef91ab51d4738c670daff901c799f6a700e27f7314029e99e348 + languageName: node + linkType: hard + +"is-glob@npm:^4.0.3": + version: 4.0.3 + resolution: "is-glob@npm:4.0.3" + dependencies: + is-extglob: ^2.1.1 + checksum: d381c1319fcb69d341cc6e6c7cd588e17cd94722d9a32dbd60660b993c4fb7d0f19438674e68dfec686d09b7c73139c9166b47597f846af387450224a8101ab4 + languageName: node + linkType: hard + +"is-json@npm:^2.0.1": + version: 2.0.1 + resolution: "is-json@npm:2.0.1" + checksum: 29efc4f82e912bf54cd7b28632dd8e52a311085ca879fe51c869a81ba1313bb689eb440ace53dd480edbc009f92a425c24059e0766f4117fe9888fe59e86186f + languageName: node + linkType: hard + +"is-lambda@npm:^1.0.1": + version: 1.0.1 + resolution: "is-lambda@npm:1.0.1" + checksum: 93a32f01940220532e5948538699ad610d5924ac86093fcee83022252b363eb0cc99ba53ab084a04e4fb62bf7b5731f55496257a4c38adf87af9c4d352c71c35 + languageName: node + linkType: hard + +"is-number@npm:^7.0.0": + version: 7.0.0 + resolution: "is-number@npm:7.0.0" + checksum: 456ac6f8e0f3111ed34668a624e45315201dff921e5ac181f8ec24923b99e9f32ca1a194912dc79d539c97d33dba17dc635202ff0b2cf98326f608323276d27a + languageName: node + linkType: hard + +"isarray@npm:0.0.1": + version: 0.0.1 + resolution: "isarray@npm:0.0.1" + checksum: 49191f1425681df4a18c2f0f93db3adb85573bcdd6a4482539d98eac9e705d8961317b01175627e860516a2fc45f8f9302db26e5a380a97a520e272e2a40a8d4 + languageName: node + linkType: hard + +"isexe@npm:^2.0.0": + version: 2.0.0 + resolution: "isexe@npm:2.0.0" + checksum: 26bf6c5480dda5161c820c5b5c751ae1e766c587b1f951ea3fcfc973bafb7831ae5b54a31a69bd670220e42e99ec154475025a468eae58ea262f813fdc8d1c62 + languageName: node + linkType: hard + +"jquery@npm:^3.6.4": + version: 3.6.4 + resolution: "jquery@npm:3.6.4" + checksum: 8354f7bd0a0424aa714ee1b6b1ef74b410f834eb5c8501682289b358bc151f11677f11188b544f3bb49309d6ec4d15d1a5de175661250c206b06185a252f706f + languageName: node + linkType: hard + +"js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0": + version: 4.0.0 + resolution: "js-tokens@npm:4.0.0" + checksum: 8a95213a5a77deb6cbe94d86340e8d9ace2b93bc367790b260101d2f36a2eaf4e4e22d9fa9cf459b38af3a32fb4190e638024cf82ec95ef708680e405ea7cc78 + languageName: node + linkType: hard + +"jsesc@npm:^2.5.1": + version: 2.5.2 + resolution: "jsesc@npm:2.5.2" + bin: + jsesc: bin/jsesc + checksum: 4dc190771129e12023f729ce20e1e0bfceac84d73a85bc3119f7f938843fe25a4aeccb54b6494dce26fcf263d815f5f31acdefac7cc9329efb8422a4f4d9fa9d + languageName: node + linkType: hard + +"jsesc@npm:~0.5.0": + version: 0.5.0 + resolution: "jsesc@npm:0.5.0" + bin: + jsesc: bin/jsesc + checksum: b8b44cbfc92f198ad972fba706ee6a1dfa7485321ee8c0b25f5cedd538dcb20cde3197de16a7265430fce8277a12db066219369e3d51055038946039f6e20e17 + languageName: node + linkType: hard + +"json-parse-even-better-errors@npm:^2.3.0": + version: 2.3.1 + resolution: "json-parse-even-better-errors@npm:2.3.1" + checksum: 798ed4cf3354a2d9ccd78e86d2169515a0097a5c133337807cdf7f1fc32e1391d207ccfc276518cc1d7d8d4db93288b8a50ba4293d212ad1336e52a8ec0a941f + languageName: node + linkType: hard + +"json5@npm:^2.2.0, json5@npm:^2.2.1": + version: 2.2.1 + resolution: "json5@npm:2.2.1" + bin: + json5: lib/cli.js + checksum: 74b8a23b102a6f2bf2d224797ae553a75488b5adbaee9c9b6e5ab8b510a2fc6e38f876d4c77dea672d4014a44b2399e15f2051ac2b37b87f74c0c7602003543b + languageName: node + linkType: hard + +"json5@npm:^2.2.2": + version: 2.2.3 + resolution: "json5@npm:2.2.3" + bin: + json5: lib/cli.js + checksum: 2a7436a93393830bce797d4626275152e37e877b265e94ca69c99e3d20c2b9dab021279146a39cdb700e71b2dd32a4cebd1514cd57cee102b1af906ce5040349 + languageName: node + linkType: hard + +"lightningcss-darwin-arm64@npm:1.19.0": + version: 1.19.0 + resolution: "lightningcss-darwin-arm64@npm:1.19.0" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"lightningcss-darwin-x64@npm:1.19.0": + version: 1.19.0 + resolution: "lightningcss-darwin-x64@npm:1.19.0" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"lightningcss-linux-arm-gnueabihf@npm:1.19.0": + version: 1.19.0 + resolution: "lightningcss-linux-arm-gnueabihf@npm:1.19.0" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"lightningcss-linux-arm64-gnu@npm:1.19.0": + version: 1.19.0 + resolution: "lightningcss-linux-arm64-gnu@npm:1.19.0" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"lightningcss-linux-arm64-musl@npm:1.19.0": + version: 1.19.0 + resolution: "lightningcss-linux-arm64-musl@npm:1.19.0" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"lightningcss-linux-x64-gnu@npm:1.19.0": + version: 1.19.0 + resolution: "lightningcss-linux-x64-gnu@npm:1.19.0" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"lightningcss-linux-x64-musl@npm:1.19.0": + version: 1.19.0 + resolution: "lightningcss-linux-x64-musl@npm:1.19.0" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"lightningcss-win32-x64-msvc@npm:1.19.0": + version: 1.19.0 + resolution: "lightningcss-win32-x64-msvc@npm:1.19.0" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"lightningcss@npm:^1.16.1": + version: 1.19.0 + resolution: "lightningcss@npm:1.19.0" + dependencies: + detect-libc: ^1.0.3 + lightningcss-darwin-arm64: 1.19.0 + lightningcss-darwin-x64: 1.19.0 + lightningcss-linux-arm-gnueabihf: 1.19.0 + lightningcss-linux-arm64-gnu: 1.19.0 + lightningcss-linux-arm64-musl: 1.19.0 + lightningcss-linux-x64-gnu: 1.19.0 + lightningcss-linux-x64-musl: 1.19.0 + lightningcss-win32-x64-msvc: 1.19.0 + dependenciesMeta: + lightningcss-darwin-arm64: + optional: true + lightningcss-darwin-x64: + optional: true + lightningcss-linux-arm-gnueabihf: + optional: true + lightningcss-linux-arm64-gnu: + optional: true + lightningcss-linux-arm64-musl: + optional: true + lightningcss-linux-x64-gnu: + optional: true + lightningcss-linux-x64-musl: + optional: true + lightningcss-win32-x64-msvc: + optional: true + checksum: c51de34b7379f9da391d0c1157893bb1484357d1ce2212a8c7943690d7a4fed7f2fa0d2dd7a92004b4444662011564ec7bf31f458a1638c856c529fe07285177 + languageName: node + linkType: hard + +"lines-and-columns@npm:^1.1.6": + version: 1.2.4 + resolution: "lines-and-columns@npm:1.2.4" + checksum: 0c37f9f7fa212b38912b7145e1cd16a5f3cd34d782441c3e6ca653485d326f58b3caccda66efce1c5812bde4961bbde3374fae4b0d11bf1226152337f3894aa5 + languageName: node + linkType: hard + +"lmdb@npm:2.5.2": + version: 2.5.2 + resolution: "lmdb@npm:2.5.2" + dependencies: + "@lmdb/lmdb-darwin-arm64": 2.5.2 + "@lmdb/lmdb-darwin-x64": 2.5.2 + "@lmdb/lmdb-linux-arm": 2.5.2 + "@lmdb/lmdb-linux-arm64": 2.5.2 + "@lmdb/lmdb-linux-x64": 2.5.2 + "@lmdb/lmdb-win32-x64": 2.5.2 + msgpackr: ^1.5.4 + node-addon-api: ^4.3.0 + node-gyp: latest + node-gyp-build-optional-packages: 5.0.3 + ordered-binary: ^1.2.4 + weak-lru-cache: ^1.2.2 + dependenciesMeta: + "@lmdb/lmdb-darwin-arm64": + optional: true + "@lmdb/lmdb-darwin-x64": + optional: true + "@lmdb/lmdb-linux-arm": + optional: true + "@lmdb/lmdb-linux-arm64": + optional: true + "@lmdb/lmdb-linux-x64": + optional: true + "@lmdb/lmdb-win32-x64": + optional: true + checksum: 3362dc2b03c6fbdfc02291001007e4096767476e65fbf8d5e332ef473946a0d108319748ef5974ebb84cf6ffa4015c039920f130bcc09c03a751b03a9fd93dff + languageName: node + linkType: hard + +"lodash.debounce@npm:^4.0.8": + version: 4.0.8 + resolution: "lodash.debounce@npm:4.0.8" + checksum: a3f527d22c548f43ae31c861ada88b2637eb48ac6aa3eb56e82d44917971b8aa96fbb37aa60efea674dc4ee8c42074f90f7b1f772e9db375435f6c83a19b3bc6 + languageName: node + linkType: hard + +"lodash.get@npm:^4.4.2": + version: 4.4.2 + resolution: "lodash.get@npm:4.4.2" + checksum: e403047ddb03181c9d0e92df9556570e2b67e0f0a930fcbbbd779370972368f5568e914f913e93f3b08f6d492abc71e14d4e9b7a18916c31fa04bd2306efe545 + languageName: node + linkType: hard + +"lodash.isequal@npm:^4.5.0": + version: 4.5.0 + resolution: "lodash.isequal@npm:4.5.0" + checksum: da27515dc5230eb1140ba65ff8de3613649620e8656b19a6270afe4866b7bd461d9ba2ac8a48dcc57f7adac4ee80e1de9f965d89d4d81a0ad52bb3eec2609644 + languageName: node + linkType: hard + +"lodash@npm:^4.17.21": + version: 4.17.21 + resolution: "lodash@npm:4.17.21" + checksum: eb835a2e51d381e561e508ce932ea50a8e5a68f4ebdd771ea240d3048244a8d13658acbd502cd4829768c56f2e16bdd4340b9ea141297d472517b83868e677f7 + languageName: node + linkType: hard + +"loose-envify@npm:^1.0.0, loose-envify@npm:^1.1.0, loose-envify@npm:^1.2.0, loose-envify@npm:^1.3.1, loose-envify@npm:^1.4.0": + version: 1.4.0 + resolution: "loose-envify@npm:1.4.0" + dependencies: + js-tokens: ^3.0.0 || ^4.0.0 + bin: + loose-envify: cli.js + checksum: 6517e24e0cad87ec9888f500c5b5947032cdfe6ef65e1c1936a0c48a524b81e65542c9c3edc91c97d5bddc806ee2a985dbc79be89215d613b1de5db6d1cfe6f4 + languageName: node + linkType: hard + +"lru-cache@npm:^5.1.1": + version: 5.1.1 + resolution: "lru-cache@npm:5.1.1" + dependencies: + yallist: ^3.0.2 + checksum: c154ae1cbb0c2206d1501a0e94df349653c92c8cbb25236d7e85190bcaf4567a03ac6eb43166fabfa36fd35623694da7233e88d9601fbf411a9a481d85dbd2cb + languageName: node + linkType: hard + +"lru-cache@npm:^6.0.0": + version: 6.0.0 + resolution: "lru-cache@npm:6.0.0" + dependencies: + yallist: ^4.0.0 + checksum: f97f499f898f23e4585742138a22f22526254fdba6d75d41a1c2526b3b6cc5747ef59c5612ba7375f42aca4f8461950e925ba08c991ead0651b4918b7c978297 + languageName: node + linkType: hard + +"lru-cache@npm:^7.7.1": + version: 7.18.3 + resolution: "lru-cache@npm:7.18.3" + checksum: e550d772384709deea3f141af34b6d4fa392e2e418c1498c078de0ee63670f1f46f5eee746e8ef7e69e1c895af0d4224e62ee33e66a543a14763b0f2e74c1356 + languageName: node + linkType: hard + +"make-fetch-happen@npm:^10.0.3": + version: 10.2.1 + resolution: "make-fetch-happen@npm:10.2.1" + dependencies: + agentkeepalive: ^4.2.1 + cacache: ^16.1.0 + http-cache-semantics: ^4.1.0 + http-proxy-agent: ^5.0.0 + https-proxy-agent: ^5.0.0 + is-lambda: ^1.0.1 + lru-cache: ^7.7.1 + minipass: ^3.1.6 + minipass-collect: ^1.0.2 + minipass-fetch: ^2.0.3 + minipass-flush: ^1.0.5 + minipass-pipeline: ^1.2.4 + negotiator: ^0.6.3 + promise-retry: ^2.0.1 + socks-proxy-agent: ^7.0.0 + ssri: ^9.0.0 + checksum: 2332eb9a8ec96f1ffeeea56ccefabcb4193693597b132cd110734d50f2928842e22b84cfa1508e921b8385cdfd06dda9ad68645fed62b50fff629a580f5fb72c + languageName: node + linkType: hard + +"mdn-data@npm:2.0.14": + version: 2.0.14 + resolution: "mdn-data@npm:2.0.14" + checksum: 9d0128ed425a89f4cba8f787dca27ad9408b5cb1b220af2d938e2a0629d17d879a34d2cb19318bdb26c3f14c77dd5dfbae67211f5caaf07b61b1f2c5c8c7dc16 + languageName: node + linkType: hard + +"memoize-one@npm:^6.0.0": + version: 6.0.0 + resolution: "memoize-one@npm:6.0.0" + checksum: f185ea69f7cceae5d1cb596266dcffccf545e8e7b4106ec6aa93b71ab9d16460dd118ac8b12982c55f6d6322fcc1485de139df07eacffaae94888b9b3ad7675f + languageName: node + linkType: hard + +"micromatch@npm:^4.0.5": + version: 4.0.5 + resolution: "micromatch@npm:4.0.5" + dependencies: + braces: ^3.0.2 + picomatch: ^2.3.1 + checksum: 02a17b671c06e8fefeeb6ef996119c1e597c942e632a21ef589154f23898c9c6a9858526246abb14f8bca6e77734aa9dcf65476fca47cedfb80d9577d52843fc + languageName: node + linkType: hard + +"minimatch@npm:^3.1.1": + version: 3.1.2 + resolution: "minimatch@npm:3.1.2" + dependencies: + brace-expansion: ^1.1.7 + checksum: c154e566406683e7bcb746e000b84d74465b3a832c45d59912b9b55cd50dee66e5c4b1e5566dba26154040e51672f9aa450a9aef0c97cfc7336b78b7afb9540a + languageName: node + linkType: hard + +"minimatch@npm:^5.0.1": + version: 5.1.6 + resolution: "minimatch@npm:5.1.6" + dependencies: + brace-expansion: ^2.0.1 + checksum: 7564208ef81d7065a370f788d337cd80a689e981042cb9a1d0e6580b6c6a8c9279eba80010516e258835a988363f99f54a6f711a315089b8b42694f5da9d0d77 + languageName: node + linkType: hard + +"minipass-collect@npm:^1.0.2": + version: 1.0.2 + resolution: "minipass-collect@npm:1.0.2" + dependencies: + minipass: ^3.0.0 + checksum: 14df761028f3e47293aee72888f2657695ec66bd7d09cae7ad558da30415fdc4752bbfee66287dcc6fd5e6a2fa3466d6c484dc1cbd986525d9393b9523d97f10 + languageName: node + linkType: hard + +"minipass-fetch@npm:^2.0.3": + version: 2.1.2 + resolution: "minipass-fetch@npm:2.1.2" + dependencies: + encoding: ^0.1.13 + minipass: ^3.1.6 + minipass-sized: ^1.0.3 + minizlib: ^2.1.2 + dependenciesMeta: + encoding: + optional: true + checksum: 3f216be79164e915fc91210cea1850e488793c740534985da017a4cbc7a5ff50506956d0f73bb0cb60e4fe91be08b6b61ef35101706d3ef5da2c8709b5f08f91 + languageName: node + linkType: hard + +"minipass-flush@npm:^1.0.5": + version: 1.0.5 + resolution: "minipass-flush@npm:1.0.5" + dependencies: + minipass: ^3.0.0 + checksum: 56269a0b22bad756a08a94b1ffc36b7c9c5de0735a4dd1ab2b06c066d795cfd1f0ac44a0fcae13eece5589b908ecddc867f04c745c7009be0b566421ea0944cf + languageName: node + linkType: hard + +"minipass-pipeline@npm:^1.2.4": + version: 1.2.4 + resolution: "minipass-pipeline@npm:1.2.4" + dependencies: + minipass: ^3.0.0 + checksum: b14240dac0d29823c3d5911c286069e36d0b81173d7bdf07a7e4a91ecdef92cdff4baaf31ea3746f1c61e0957f652e641223970870e2353593f382112257971b + languageName: node + linkType: hard + +"minipass-sized@npm:^1.0.3": + version: 1.0.3 + resolution: "minipass-sized@npm:1.0.3" + dependencies: + minipass: ^3.0.0 + checksum: 79076749fcacf21b5d16dd596d32c3b6bf4d6e62abb43868fac21674078505c8b15eaca4e47ed844985a4514854f917d78f588fcd029693709417d8f98b2bd60 + languageName: node + linkType: hard + +"minipass@npm:^3.0.0, minipass@npm:^3.1.1, minipass@npm:^3.1.6": + version: 3.3.6 + resolution: "minipass@npm:3.3.6" + dependencies: + yallist: ^4.0.0 + checksum: a30d083c8054cee83cdcdc97f97e4641a3f58ae743970457b1489ce38ee1167b3aaf7d815cd39ec7a99b9c40397fd4f686e83750e73e652b21cb516f6d845e48 + languageName: node + linkType: hard + +"minipass@npm:^4.0.0": + version: 4.2.5 + resolution: "minipass@npm:4.2.5" + checksum: 4f9c19af23a5d4a9e7156feefc9110634b178a8cff8f8271af16ec5ebf7e221725a97429952c856f5b17b30c2065ebd24c81722d90c93d2122611d75b952b48f + languageName: node + linkType: hard + +"minizlib@npm:^2.1.1, minizlib@npm:^2.1.2": + version: 2.1.2 + resolution: "minizlib@npm:2.1.2" + dependencies: + minipass: ^3.0.0 + yallist: ^4.0.0 + checksum: f1fdeac0b07cf8f30fcf12f4b586795b97be856edea22b5e9072707be51fc95d41487faec3f265b42973a304fe3a64acd91a44a3826a963e37b37bafde0212c3 + languageName: node + linkType: hard + +"mkdirp@npm:^1.0.3, mkdirp@npm:^1.0.4": + version: 1.0.4 + resolution: "mkdirp@npm:1.0.4" + bin: + mkdirp: bin/cmd.js + checksum: a96865108c6c3b1b8e1d5e9f11843de1e077e57737602de1b82030815f311be11f96f09cce59bd5b903d0b29834733e5313f9301e3ed6d6f6fba2eae0df4298f + languageName: node + linkType: hard + +"ms@npm:2.1.2": + version: 2.1.2 + resolution: "ms@npm:2.1.2" + checksum: 673cdb2c3133eb050c745908d8ce632ed2c02d85640e2edb3ace856a2266a813b30c613569bf3354fdf4ea7d1a1494add3bfa95e2713baa27d0c2c71fc44f58f + languageName: node + linkType: hard + +"ms@npm:^2.0.0": + version: 2.1.3 + resolution: "ms@npm:2.1.3" + checksum: aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d + languageName: node + linkType: hard + +"msgpackr-extract@npm:^2.0.2": + version: 2.0.2 + resolution: "msgpackr-extract@npm:2.0.2" + dependencies: + "@msgpackr-extract/msgpackr-extract-darwin-arm64": 2.0.2 + "@msgpackr-extract/msgpackr-extract-darwin-x64": 2.0.2 + "@msgpackr-extract/msgpackr-extract-linux-arm": 2.0.2 + "@msgpackr-extract/msgpackr-extract-linux-arm64": 2.0.2 + "@msgpackr-extract/msgpackr-extract-linux-x64": 2.0.2 + "@msgpackr-extract/msgpackr-extract-win32-x64": 2.0.2 + node-gyp: latest + node-gyp-build-optional-packages: 5.0.2 + dependenciesMeta: + "@msgpackr-extract/msgpackr-extract-darwin-arm64": + optional: true + "@msgpackr-extract/msgpackr-extract-darwin-x64": + optional: true + "@msgpackr-extract/msgpackr-extract-linux-arm": + optional: true + "@msgpackr-extract/msgpackr-extract-linux-arm64": + optional: true + "@msgpackr-extract/msgpackr-extract-linux-x64": + optional: true + "@msgpackr-extract/msgpackr-extract-win32-x64": + optional: true + checksum: 6b24c0e89eae881012484787082a50290f78d2dc69df28c28838c65f9cda3f585272c73b9ebbf386f9958c16a9956f0cabddf2ccfc1229ee612a6b88e9519c68 + languageName: node + linkType: hard + +"msgpackr@npm:^1.5.4": + version: 1.6.1 + resolution: "msgpackr@npm:1.6.1" + dependencies: + msgpackr-extract: ^2.0.2 + dependenciesMeta: + msgpackr-extract: + optional: true + checksum: 1c34b1c314e8db2bcb6debb10034bd965f6333812d7a29248745af8e0204cc571484e48bbc09b6cef4e244523acb32db5e9e2637aafbebf03d97bb3898657800 + languageName: node + linkType: hard + +"negotiator@npm:^0.6.3": + version: 0.6.3 + resolution: "negotiator@npm:0.6.3" + checksum: b8ffeb1e262eff7968fc90a2b6767b04cfd9842582a9d0ece0af7049537266e7b2506dfb1d107a32f06dd849ab2aea834d5830f7f4d0e5cb7d36e1ae55d021d9 + languageName: node + linkType: hard + +"node-addon-api@npm:^3.2.1": + version: 3.2.1 + resolution: "node-addon-api@npm:3.2.1" + dependencies: + node-gyp: latest + checksum: 2369986bb0881ccd9ef6bacdf39550e07e089a9c8ede1cbc5fc7712d8e2faa4d50da0e487e333d4125f8c7a616c730131d1091676c9d499af1d74560756b4a18 + languageName: node + linkType: hard + +"node-addon-api@npm:^4.3.0": + version: 4.3.0 + resolution: "node-addon-api@npm:4.3.0" + dependencies: + node-gyp: latest + checksum: 3de396e23cc209f539c704583e8e99c148850226f6e389a641b92e8967953713228109f919765abc1f4355e801e8f41842f96210b8d61c7dcc10a477002dcf00 + languageName: node + linkType: hard + +"node-gyp-build-optional-packages@npm:5.0.2": + version: 5.0.2 + resolution: "node-gyp-build-optional-packages@npm:5.0.2" + bin: + node-gyp-build-optional: optional.js + node-gyp-build-optional-packages: bin.js + node-gyp-build-test: build-test.js + checksum: 6fca33cd1e297a446dead8a9bc7a48988be30098c219e75e8466d0218dea6b03bf5da092fe20301cb8b72218356c656422f1a64520c37ebc30eb596b012d1ad9 + languageName: node + linkType: hard + +"node-gyp-build-optional-packages@npm:5.0.3": + version: 5.0.3 + resolution: "node-gyp-build-optional-packages@npm:5.0.3" + bin: + node-gyp-build-optional-packages: bin.js + node-gyp-build-optional-packages-optional: optional.js + node-gyp-build-optional-packages-test: build-test.js + checksum: be3f0235925c8361e5bc1a03848f5e24815b0df8aa90bd13f1eac91cd86264bbb8b7689ca6cd083b02c8099c7b54f9fb83066c7bb77c2389dc4eceab921f084f + languageName: node + linkType: hard + +"node-gyp-build@npm:^4.3.0": + version: 4.4.0 + resolution: "node-gyp-build@npm:4.4.0" + bin: + node-gyp-build: bin.js + node-gyp-build-optional: optional.js + node-gyp-build-test: build-test.js + checksum: 972a059f960253d254e0b23ce10f54c8982236fc0edcab85166d0b7f87443b2ce98391c877cfb2f6eeafcf03c538c5f4dd3e0bfff03828eb48634f58f4c64343 + languageName: node + linkType: hard + +"node-gyp@npm:latest": + version: 9.3.1 + resolution: "node-gyp@npm:9.3.1" + dependencies: + env-paths: ^2.2.0 + glob: ^7.1.4 + graceful-fs: ^4.2.6 + make-fetch-happen: ^10.0.3 + nopt: ^6.0.0 + npmlog: ^6.0.0 + rimraf: ^3.0.2 + semver: ^7.3.5 + tar: ^6.1.2 + which: ^2.0.2 + bin: + node-gyp: bin/node-gyp.js + checksum: b860e9976fa645ca0789c69e25387401b4396b93c8375489b5151a6c55cf2640a3b6183c212b38625ef7c508994930b72198338e3d09b9d7ade5acc4aaf51ea7 + languageName: node + linkType: hard + +"node-releases@npm:^2.0.5": + version: 2.0.5 + resolution: "node-releases@npm:2.0.5" + checksum: e85d949addd19f8827f32569d2be5751e7812ccf6cc47879d49f79b5234ff4982225e39a3929315f96370823b070640fb04d79fc0ddec8b515a969a03493a42f + languageName: node + linkType: hard + +"node-releases@npm:^2.0.8": + version: 2.0.10 + resolution: "node-releases@npm:2.0.10" + checksum: d784ecde25696a15d449c4433077f5cce620ed30a1656c4abf31282bfc691a70d9618bae6868d247a67914d1be5cc4fde22f65a05f4398cdfb92e0fc83cadfbc + languageName: node + linkType: hard + +"nopt@npm:^6.0.0": + version: 6.0.0 + resolution: "nopt@npm:6.0.0" + dependencies: + abbrev: ^1.0.0 + bin: + nopt: bin/nopt.js + checksum: 82149371f8be0c4b9ec2f863cc6509a7fd0fa729929c009f3a58e4eb0c9e4cae9920e8f1f8eb46e7d032fec8fb01bede7f0f41a67eb3553b7b8e14fa53de1dac + languageName: node + linkType: hard + +"npmlog@npm:^6.0.0": + version: 6.0.2 + resolution: "npmlog@npm:6.0.2" + dependencies: + are-we-there-yet: ^3.0.0 + console-control-strings: ^1.1.0 + gauge: ^4.0.3 + set-blocking: ^2.0.0 + checksum: ae238cd264a1c3f22091cdd9e2b106f684297d3c184f1146984ecbe18aaa86343953f26b9520dedd1b1372bc0316905b736c1932d778dbeb1fcf5a1001390e2a + languageName: node + linkType: hard + +"nth-check@npm:^2.0.1": + version: 2.1.1 + resolution: "nth-check@npm:2.1.1" + dependencies: + boolbase: ^1.0.0 + checksum: 5afc3dafcd1573b08877ca8e6148c52abd565f1d06b1eb08caf982e3fa289a82f2cae697ffb55b5021e146d60443f1590a5d6b944844e944714a5b549675bcd3 + languageName: node + linkType: hard + +"nullthrows@npm:^1.1.1": + version: 1.1.1 + resolution: "nullthrows@npm:1.1.1" + checksum: 10806b92121253eb1b08ecf707d92480f5331ba8ae5b23fa3eb0548ad24196eb797ed47606153006568a5733ea9e528a3579f21421f7828e09e7756f4bdd386f + languageName: node + linkType: hard + +"object-assign@npm:^4.1.0, object-assign@npm:^4.1.1": + version: 4.1.1 + resolution: "object-assign@npm:4.1.1" + checksum: fcc6e4ea8c7fe48abfbb552578b1c53e0d194086e2e6bbbf59e0a536381a292f39943c6e9628af05b5528aa5e3318bb30d6b2e53cadaf5b8fe9e12c4b69af23f + languageName: node + linkType: hard + +"once@npm:^1.3.0": + version: 1.4.0 + resolution: "once@npm:1.4.0" + dependencies: + wrappy: 1 + checksum: cd0a88501333edd640d95f0d2700fbde6bff20b3d4d9bdc521bdd31af0656b5706570d6c6afe532045a20bb8dc0849f8332d6f2a416e0ba6d3d3b98806c7db68 + languageName: node + linkType: hard + +"ordered-binary@npm:^1.2.4": + version: 1.2.5 + resolution: "ordered-binary@npm:1.2.5" + checksum: fd0f1322a67064fa7e35bada142b05c50442976907e834a0c4d19b64aa621cd6941fc3d0f87eede91359ace9a932119f3c2248eb43fd52e6103ba1210aa3c506 + languageName: node + linkType: hard + +"p-map@npm:^4.0.0": + version: 4.0.0 + resolution: "p-map@npm:4.0.0" + dependencies: + aggregate-error: ^3.0.0 + checksum: cb0ab21ec0f32ddffd31dfc250e3afa61e103ef43d957cc45497afe37513634589316de4eb88abdfd969fe6410c22c0b93ab24328833b8eb1ccc087fc0442a1c + languageName: node + linkType: hard + +"parcel@npm:^2.8.3": + version: 2.8.3 + resolution: "parcel@npm:2.8.3" + dependencies: + "@parcel/config-default": 2.8.3 + "@parcel/core": 2.8.3 + "@parcel/diagnostic": 2.8.3 + "@parcel/events": 2.8.3 + "@parcel/fs": 2.8.3 + "@parcel/logger": 2.8.3 + "@parcel/package-manager": 2.8.3 + "@parcel/reporter-cli": 2.8.3 + "@parcel/reporter-dev-server": 2.8.3 + "@parcel/utils": 2.8.3 + chalk: ^4.1.0 + commander: ^7.0.0 + get-port: ^4.2.0 + v8-compile-cache: ^2.0.0 + bin: + parcel: lib/bin.js + checksum: 09cd2dc23c2ec0417e9de93face185a08679d744c6cbb627fce6ffb507f8af1f8d0642f063e0cf771b699419a29db8ee7ca60cdb32966a65dd3b03da35473bfa + languageName: node + linkType: hard + +"parent-module@npm:^1.0.0": + version: 1.0.1 + resolution: "parent-module@npm:1.0.1" + dependencies: + callsites: ^3.0.0 + checksum: 6ba8b255145cae9470cf5551eb74be2d22281587af787a2626683a6c20fbb464978784661478dd2a3f1dad74d1e802d403e1b03c1a31fab310259eec8ac560ff + languageName: node + linkType: hard + +"parse-json@npm:^5.0.0": + version: 5.2.0 + resolution: "parse-json@npm:5.2.0" + dependencies: + "@babel/code-frame": ^7.0.0 + error-ex: ^1.3.1 + json-parse-even-better-errors: ^2.3.0 + lines-and-columns: ^1.1.6 + checksum: 62085b17d64da57f40f6afc2ac1f4d95def18c4323577e1eced571db75d9ab59b297d1d10582920f84b15985cbfc6b6d450ccbf317644cfa176f3ed982ad87e2 + languageName: node + linkType: hard + +"path-is-absolute@npm:^1.0.0": + version: 1.0.1 + resolution: "path-is-absolute@npm:1.0.1" + checksum: 060840f92cf8effa293bcc1bea81281bd7d363731d214cbe5c227df207c34cd727430f70c6037b5159c8a870b9157cba65e775446b0ab06fd5ecc7e54615a3b8 + languageName: node + linkType: hard + +"path-parse@npm:^1.0.7": + version: 1.0.7 + resolution: "path-parse@npm:1.0.7" + checksum: 49abf3d81115642938a8700ec580da6e830dde670be21893c62f4e10bd7dd4c3742ddc603fe24f898cba7eb0c6bc1777f8d9ac14185d34540c6d4d80cd9cae8a + languageName: node + linkType: hard + +"path-to-regexp@npm:^1.7.0": + version: 1.7.0 + resolution: "path-to-regexp@npm:1.7.0" + dependencies: + isarray: 0.0.1 + checksum: bf5054aa006508b4f9fa9603f091ce90cb448f5a2714ce8b5dfefce761f107ca3a029401c28bf5824e4f38b71a605b3c24bd92e8a284c6436ab3060a497dd7ad + languageName: node + linkType: hard + +"path-type@npm:^4.0.0": + version: 4.0.0 + resolution: "path-type@npm:4.0.0" + checksum: 5b1e2daa247062061325b8fdbfd1fb56dde0a448fb1455453276ea18c60685bdad23a445dc148cf87bc216be1573357509b7d4060494a6fd768c7efad833ee45 + languageName: node + linkType: hard + +"picocolors@npm:^1.0.0": + version: 1.0.0 + resolution: "picocolors@npm:1.0.0" + checksum: a2e8092dd86c8396bdba9f2b5481032848525b3dc295ce9b57896f931e63fc16f79805144321f72976383fc249584672a75cc18d6777c6b757603f372f745981 + languageName: node + linkType: hard + +"picomatch@npm:^2.3.1": + version: 2.3.1 + resolution: "picomatch@npm:2.3.1" + checksum: 050c865ce81119c4822c45d3c84f1ced46f93a0126febae20737bd05ca20589c564d6e9226977df859ed5e03dc73f02584a2b0faad36e896936238238b0446cf + languageName: node + linkType: hard + +"popper.js@npm:^1.16.1": + version: 1.16.1 + resolution: "popper.js@npm:1.16.1" + checksum: c56ae5001ec50a77ee297a8061a0221d99d25c7348d2e6bcd3e45a0d0f32a1fd81bca29d46cb0d4bdf13efb77685bd6a0ce93f9eb3c608311a461f945fffedbe + languageName: node + linkType: hard + +"postcss-value-parser@npm:^4.2.0": + version: 4.2.0 + resolution: "postcss-value-parser@npm:4.2.0" + checksum: 819ffab0c9d51cf0acbabf8996dffbfafbafa57afc0e4c98db88b67f2094cb44488758f06e5da95d7036f19556a4a732525e84289a425f4f6fd8e412a9d7442f + languageName: node + linkType: hard + +"posthtml-parser@npm:^0.10.1": + version: 0.10.2 + resolution: "posthtml-parser@npm:0.10.2" + dependencies: + htmlparser2: ^7.1.1 + checksum: 63ec8e8631031f7879cada68ad165436ad6142eedd6ed9cb19b28c87848985819d50104d73a182a5205e7083e93131b68196c13c32cea12c0e225c7400591432 + languageName: node + linkType: hard + +"posthtml-parser@npm:^0.11.0": + version: 0.11.0 + resolution: "posthtml-parser@npm:0.11.0" + dependencies: + htmlparser2: ^7.1.1 + checksum: 37dca546a04dc2ddc936a629596edccc9e439a7f6ad503dae5165ea197ddc53f102e69259719a49ecd491e01b093b95c96287c38101f985b78a846c05a206b3c + languageName: node + linkType: hard + +"posthtml-render@npm:^3.0.0": + version: 3.0.0 + resolution: "posthtml-render@npm:3.0.0" + dependencies: + is-json: ^2.0.1 + checksum: 5ed2d6e8813af63c4e5a2d9d026f611fd178c9052a16b302a6e0e81d1badb64dab36e3fc1531b5bdd376465f39d19a6488299b3c6dfe13beae3dd525ff856573 + languageName: node + linkType: hard + +"posthtml@npm:^0.16.4, posthtml@npm:^0.16.5": + version: 0.16.6 + resolution: "posthtml@npm:0.16.6" + dependencies: + posthtml-parser: ^0.11.0 + posthtml-render: ^3.0.0 + checksum: 8b9b9d27bd2417d6b5b7d408000b23316c3c4d2a2d0ea62080a8fbec5654cc7376ea9d6317b290c030d616142144a8ca0a96ffe1e919493e3eac17442d362596 + languageName: node + linkType: hard + +"process@npm:^0.11.10": + version: 0.11.10 + resolution: "process@npm:0.11.10" + checksum: bfcce49814f7d172a6e6a14d5fa3ac92cc3d0c3b9feb1279774708a719e19acd673995226351a082a9ae99978254e320ccda4240ddc474ba31a76c79491ca7c3 + languageName: node + linkType: hard + +"promise-inflight@npm:^1.0.1": + version: 1.0.1 + resolution: "promise-inflight@npm:1.0.1" + checksum: 22749483091d2c594261517f4f80e05226d4d5ecc1fc917e1886929da56e22b5718b7f2a75f3807e7a7d471bc3be2907fe92e6e8f373ddf5c64bae35b5af3981 + languageName: node + linkType: hard + +"promise-retry@npm:^2.0.1": + version: 2.0.1 + resolution: "promise-retry@npm:2.0.1" + dependencies: + err-code: ^2.0.2 + retry: ^0.12.0 + checksum: f96a3f6d90b92b568a26f71e966cbbc0f63ab85ea6ff6c81284dc869b41510e6cdef99b6b65f9030f0db422bf7c96652a3fff9f2e8fb4a0f069d8f4430359429 + languageName: node + linkType: hard + +"prop-types@npm:^15.5.8, prop-types@npm:^15.6.2": + version: 15.6.2 + resolution: "prop-types@npm:15.6.2" + dependencies: + loose-envify: ^1.3.1 + object-assign: ^4.1.1 + checksum: 79e478b2684449295bc8c60af1cfba4f3d414a5d832e9d23f720bce7f07df9dc52105a626134d9a3f84bb5551c9da5ec6d77d10f906b5cef26fe464959b96646 + languageName: node + linkType: hard + +"prop-types@npm:^15.7.2": + version: 15.7.2 + resolution: "prop-types@npm:15.7.2" + dependencies: + loose-envify: ^1.4.0 + object-assign: ^4.1.1 + react-is: ^16.8.1 + checksum: 5eef82fdda64252c7e75aa5c8cc28a24bbdece0f540adb60ce67c205cf978a5bd56b83e4f269f91c6e4dcfd80b36f2a2dec24d362e278913db2086ca9c6f9430 + languageName: node + linkType: hard + +"prop-types@npm:^15.8.1": + version: 15.8.1 + resolution: "prop-types@npm:15.8.1" + dependencies: + loose-envify: ^1.4.0 + object-assign: ^4.1.1 + react-is: ^16.13.1 + checksum: c056d3f1c057cb7ff8344c645450e14f088a915d078dcda795041765047fa080d38e5d626560ccaac94a4e16e3aa15f3557c1a9a8d1174530955e992c675e459 + languageName: node + linkType: hard + +"react-ace@npm:^10.1.0": + version: 10.1.0 + resolution: "react-ace@npm:10.1.0" + dependencies: + ace-builds: ^1.4.14 + diff-match-patch: ^1.0.5 + lodash.get: ^4.4.2 + lodash.isequal: ^4.5.0 + prop-types: ^15.7.2 + peerDependencies: + react: ^0.13.0 || ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 + react-dom: ^0.13.0 || ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 + checksum: 8fc67e02f911dcc31cf474df97c06f2ba5b5f402d9ed425895e479e2daddf4894a2cd0e81744b37b7f2b07109cfe4470fc5ebeed305d648c596e3442142e4823 + languageName: node + linkType: hard + +"react-contextmenu@npm:^2.14.0": + version: 2.14.0 + resolution: "react-contextmenu@npm:2.14.0" + dependencies: + classnames: ^2.2.5 + object-assign: ^4.1.0 + peerDependencies: + prop-types: ^15.0.0 + react: ^0.14.0 || ^15.0.0 || ^16.0.1 + react-dom: ^0.14.0 || ^15.0.0 || ^16.0.1 + checksum: a250ed92535b339fb5dd8ec11b6a75e28b46fe46b43771ea4fefad6ca88afc44a157620c1596ced90c9fc9fe5270502f4cd1d0b29d71e698ec93a87b3c43b13e + languageName: node + linkType: hard + +"react-dom@npm:^18.2.0": + version: 18.2.0 + resolution: "react-dom@npm:18.2.0" + dependencies: + loose-envify: ^1.1.0 + scheduler: ^0.23.0 + peerDependencies: + react: ^18.2.0 + checksum: 7d323310bea3a91be2965f9468d552f201b1c27891e45ddc2d6b8f717680c95a75ae0bc1e3f5cf41472446a2589a75aed4483aee8169287909fcd59ad149e8cc + languageName: node + linkType: hard + +"react-error-overlay@npm:6.0.9": + version: 6.0.9 + resolution: "react-error-overlay@npm:6.0.9" + checksum: 695853bc885e798008a00c10d8d94e5ac91626e8130802fea37345f9c037f41b80104345db2ee87f225feb4a4ef71b0df572b17c378a6d397b6815f6d4a84293 + languageName: node + linkType: hard + +"react-fast-compare@npm:^3.0.1": + version: 3.2.1 + resolution: "react-fast-compare@npm:3.2.1" + checksum: 209b4dc3a9cc79c074a26ec020459efd8be279accaca612db2edb8ada2a28849ea51cf3d246fc0fafb344949b93a63a43798b6c1787559b0a128571883fe6859 + languageName: node + linkType: hard + +"react-is@npm:^16.13.1, react-is@npm:^16.6.0, react-is@npm:^16.7.0, react-is@npm:^16.8.1": + version: 16.13.1 + resolution: "react-is@npm:16.13.1" + checksum: f7a19ac3496de32ca9ae12aa030f00f14a3d45374f1ceca0af707c831b2a6098ef0d6bdae51bd437b0a306d7f01d4677fcc8de7c0d331eb47ad0f46130e53c5f + languageName: node + linkType: hard + +"react-popper@npm:^2.2.4": + version: 2.3.0 + resolution: "react-popper@npm:2.3.0" + dependencies: + react-fast-compare: ^3.0.1 + warning: ^4.0.2 + peerDependencies: + "@popperjs/core": ^2.0.0 + react: ^16.8.0 || ^17 || ^18 + react-dom: ^16.8.0 || ^17 || ^18 + checksum: 837111c98738011c69b3069a464ea5bdcbf487105b6148e8faf90cb7337e134edb1b98b8824322941c378756cca30a15c18c25f558e53b85ed5762fa0dc8e6b2 + languageName: node + linkType: hard + +"react-refresh@npm:^0.9.0": + version: 0.9.0 + resolution: "react-refresh@npm:0.9.0" + checksum: 6440146176f19402ffb7d66f317e40b1c42c88579b4d439b49021e38be6307c642da3e8732a72e6997b6bb1127db0da92f4aa433da4313ce8ebad0c1efa2ed4a + languageName: node + linkType: hard + +"react-router-dom@npm:^5.3.4": + version: 5.3.4 + resolution: "react-router-dom@npm:5.3.4" + dependencies: + "@babel/runtime": ^7.12.13 + history: ^4.9.0 + loose-envify: ^1.3.1 + prop-types: ^15.6.2 + react-router: 5.3.4 + tiny-invariant: ^1.0.2 + tiny-warning: ^1.0.0 + peerDependencies: + react: ">=15" + checksum: b86a6f2f5222f041e38adf4e4b32c7643d6735a1a915ef25855b2db285fd059d72ba8d62e5bcd5d822b8ef9520a80453209e55077f5a90d0f72e908979b8f535 + languageName: node + linkType: hard + +"react-router@npm:5.3.4": + version: 5.3.4 + resolution: "react-router@npm:5.3.4" + dependencies: + "@babel/runtime": ^7.12.13 + history: ^4.9.0 + hoist-non-react-statics: ^3.1.0 + loose-envify: ^1.3.1 + path-to-regexp: ^1.7.0 + prop-types: ^15.6.2 + react-is: ^16.6.0 + tiny-invariant: ^1.0.2 + tiny-warning: ^1.0.0 + peerDependencies: + react: ">=15" + checksum: 892d4e274a23bf4f39abc2efca54472fb646d3aed4b584020cf49654d2f50d09a2bacebe7c92b4ec7cb8925077376dfcd0664bad6442a73604397cefec9f01f9 + languageName: node + linkType: hard + +"react-table@npm:^6.11.5": + version: 6.11.5 + resolution: "react-table@npm:6.11.5" + dependencies: + "@types/react-table": ^6.8.5 + classnames: ^2.2.5 + react-is: ^16.8.1 + peerDependencies: + prop-types: ^15.7.0 + react: ^16.x.x + react-dom: ^16.x.x + checksum: dda0e7f661664fe7e6331a2b518772160923195c6b9beb56f937cc1d617690d4124e31d4d4c6c1b9068f57b59ae00ae3b35c82a04f633ca2cfd2de83d5fd6da2 + languageName: node + linkType: hard + +"react-transition-group@npm:^4.4.2": + version: 4.4.5 + resolution: "react-transition-group@npm:4.4.5" + dependencies: + "@babel/runtime": ^7.5.5 + dom-helpers: ^5.0.1 + loose-envify: ^1.4.0 + prop-types: ^15.6.2 + peerDependencies: + react: ">=16.6.0" + react-dom: ">=16.6.0" + checksum: 75602840106aa9c6545149d6d7ae1502fb7b7abadcce70a6954c4b64a438ff1cd16fc77a0a1e5197cdd72da398f39eb929ea06f9005c45b132ed34e056ebdeb1 + languageName: node + linkType: hard + +"react@npm:^18.2.0": + version: 18.2.0 + resolution: "react@npm:18.2.0" + dependencies: + loose-envify: ^1.1.0 + checksum: 88e38092da8839b830cda6feef2e8505dec8ace60579e46aa5490fc3dc9bba0bd50336507dc166f43e3afc1c42939c09fe33b25fae889d6f402721dcd78fca1b + languageName: node + linkType: hard + +"reactdom@npm:^2.0.0": + version: 2.0.0 + resolution: "reactdom@npm:2.0.0" + checksum: c87b3fdc8eb173cc32199e47107bf46c757f96dc747ca3331e9dcd71de41ce7e513c97c1a8786d63c06e59e1e3cf5973cf94c54d9be44eed83d342476e3631de + languageName: node + linkType: hard + +"reactstrap@npm:^9.1.6": + version: 9.1.6 + resolution: "reactstrap@npm:9.1.6" + dependencies: + "@babel/runtime": ^7.12.5 + "@popperjs/core": ^2.6.0 + classnames: ^2.2.3 + prop-types: ^15.5.8 + react-popper: ^2.2.4 + react-transition-group: ^4.4.2 + peerDependencies: + react: ">=16.8.0" + react-dom: ">=16.8.0" + checksum: 32339cf1abd8403744134a1d449e71484b057176160cc62c5c30c14d72ab0d2d7b5704321e16341908d050a53e3ab0ec0bc7c7d15824ca73e953687c02a4e063 + languageName: node + linkType: hard + +"readable-stream@npm:^3.6.0": + version: 3.6.2 + resolution: "readable-stream@npm:3.6.2" + dependencies: + inherits: ^2.0.3 + string_decoder: ^1.1.1 + util-deprecate: ^1.0.1 + checksum: bdcbe6c22e846b6af075e32cf8f4751c2576238c5043169a1c221c92ee2878458a816a4ea33f4c67623c0b6827c8a400409bfb3cf0bf3381392d0b1dfb52ac8d + languageName: node + linkType: hard + +"regenerate-unicode-properties@npm:^10.0.1": + version: 10.0.1 + resolution: "regenerate-unicode-properties@npm:10.0.1" + dependencies: + regenerate: ^1.4.2 + checksum: 1b638b7087d8143e5be3e20e2cda197ea0440fa0bc2cc49646b2f50c5a2b1acdc54b21e4215805a5a2dd487c686b2291accd5ad00619534098d2667e76247754 + languageName: node + linkType: hard + +"regenerate-unicode-properties@npm:^10.1.0": + version: 10.1.0 + resolution: "regenerate-unicode-properties@npm:10.1.0" + dependencies: + regenerate: ^1.4.2 + checksum: b1a8929588433ab8b9dc1a34cf3665b3b472f79f2af6ceae00d905fc496b332b9af09c6718fb28c730918f19a00dc1d7310adbaa9b72a2ec7ad2f435da8ace17 + languageName: node + linkType: hard + +"regenerate@npm:^1.4.2": + version: 1.4.2 + resolution: "regenerate@npm:1.4.2" + checksum: 3317a09b2f802da8db09aa276e469b57a6c0dd818347e05b8862959c6193408242f150db5de83c12c3fa99091ad95fb42a6db2c3329bfaa12a0ea4cbbeb30cb0 + languageName: node + linkType: hard + +"regenerator-runtime@npm:^0.13.11": + version: 0.13.11 + resolution: "regenerator-runtime@npm:0.13.11" + checksum: 27481628d22a1c4e3ff551096a683b424242a216fee44685467307f14d58020af1e19660bf2e26064de946bad7eff28950eae9f8209d55723e2d9351e632bbb4 + languageName: node + linkType: hard + +"regenerator-runtime@npm:^0.13.4, regenerator-runtime@npm:^0.13.7": + version: 0.13.9 + resolution: "regenerator-runtime@npm:0.13.9" + checksum: 65ed455fe5afd799e2897baf691ca21c2772e1a969d19bb0c4695757c2d96249eb74ee3553ea34a91062b2a676beedf630b4c1551cc6299afb937be1426ec55e + languageName: node + linkType: hard + +"regenerator-transform@npm:^0.15.1": + version: 0.15.1 + resolution: "regenerator-transform@npm:0.15.1" + dependencies: + "@babel/runtime": ^7.8.4 + checksum: 2d15bdeadbbfb1d12c93f5775493d85874dbe1d405bec323da5c61ec6e701bc9eea36167483e1a5e752de9b2df59ab9a2dfff6bf3784f2b28af2279a673d29a4 + languageName: node + linkType: hard + +"regexpu-core@npm:^5.0.1": + version: 5.0.1 + resolution: "regexpu-core@npm:5.0.1" + dependencies: + regenerate: ^1.4.2 + regenerate-unicode-properties: ^10.0.1 + regjsgen: ^0.6.0 + regjsparser: ^0.8.2 + unicode-match-property-ecmascript: ^2.0.0 + unicode-match-property-value-ecmascript: ^2.0.0 + checksum: 6151a9700dad512fadb5564ad23246d54c880eb9417efa5e5c3658b910c1ff894d622dfd159af2ed527ffd44751bfe98682ae06c717155c254d8e2b4bab62785 + languageName: node + linkType: hard + +"regexpu-core@npm:^5.3.1": + version: 5.3.2 + resolution: "regexpu-core@npm:5.3.2" + dependencies: + "@babel/regjsgen": ^0.8.0 + regenerate: ^1.4.2 + regenerate-unicode-properties: ^10.1.0 + regjsparser: ^0.9.1 + unicode-match-property-ecmascript: ^2.0.0 + unicode-match-property-value-ecmascript: ^2.1.0 + checksum: 95bb97088419f5396e07769b7de96f995f58137ad75fac5811fb5fe53737766dfff35d66a0ee66babb1eb55386ef981feaef392f9df6d671f3c124812ba24da2 + languageName: node + linkType: hard + +"regjsgen@npm:^0.6.0": + version: 0.6.0 + resolution: "regjsgen@npm:0.6.0" + checksum: c5158ebd735e75074e41292ade1ff05d85566d205426cc61501e360c450a63baced8512ee3ae238e5c0a0e42969563c7875b08fa69d6f0402daf36bcb3e4d348 + languageName: node + linkType: hard + +"regjsparser@npm:^0.8.2": + version: 0.8.4 + resolution: "regjsparser@npm:0.8.4" + dependencies: + jsesc: ~0.5.0 + bin: + regjsparser: bin/parser + checksum: d069b932491761cda127ce11f6bd2729c3b1b394a35200ec33f1199e937423db28ceb86cf33f0a97c76ecd7c0f8db996476579eaf0d80a1f74c1934f4ca8b27a + languageName: node + linkType: hard + +"regjsparser@npm:^0.9.1": + version: 0.9.1 + resolution: "regjsparser@npm:0.9.1" + dependencies: + jsesc: ~0.5.0 + bin: + regjsparser: bin/parser + checksum: 5e1b76afe8f1d03c3beaf9e0d935dd467589c3625f6d65fb8ffa14f224d783a0fed4bf49c2c1b8211043ef92b6117313419edf055a098ed8342e340586741afc + languageName: node + linkType: hard + +"resolve-from@npm:^4.0.0": + version: 4.0.0 + resolution: "resolve-from@npm:4.0.0" + checksum: f4ba0b8494846a5066328ad33ef8ac173801a51739eb4d63408c847da9a2e1c1de1e6cbbf72699211f3d13f8fc1325648b169bd15eb7da35688e30a5fb0e4a7f + languageName: node + linkType: hard + +"resolve-pathname@npm:^3.0.0": + version: 3.0.0 + resolution: "resolve-pathname@npm:3.0.0" + checksum: 6147241ba42c423dbe83cb067a2b4af4f60908c3af57e1ea567729cc71416c089737fe2a73e9e79e7a60f00f66c91e4b45ad0d37cd4be2d43fec44963ef14368 + languageName: node + linkType: hard + +"resolve@npm:^1.14.2": + version: 1.22.0 + resolution: "resolve@npm:1.22.0" + dependencies: + is-core-module: ^2.8.1 + path-parse: ^1.0.7 + supports-preserve-symlinks-flag: ^1.0.0 + bin: + resolve: bin/resolve + checksum: a2d14cc437b3a23996f8c7367eee5c7cf8149c586b07ca2ae00e96581ce59455555a1190be9aa92154785cf9f2042646c200d0e00e0bbd2b8a995a93a0ed3e4e + languageName: node + linkType: hard + +"resolve@patch:resolve@^1.14.2#~builtin": + version: 1.22.0 + resolution: "resolve@patch:resolve@npm%3A1.22.0#~builtin::version=1.22.0&hash=c3c19d" + dependencies: + is-core-module: ^2.8.1 + path-parse: ^1.0.7 + supports-preserve-symlinks-flag: ^1.0.0 + bin: + resolve: bin/resolve + checksum: c79ecaea36c872ee4a79e3db0d3d4160b593f2ca16e031d8283735acd01715a203607e9ded3f91f68899c2937fa0d49390cddbe0fb2852629212f3cda283f4a7 + languageName: node + linkType: hard + +"retry@npm:^0.12.0": + version: 0.12.0 + resolution: "retry@npm:0.12.0" + checksum: 623bd7d2e5119467ba66202d733ec3c2e2e26568074923bc0585b6b99db14f357e79bdedb63cab56cec47491c4a0da7e6021a7465ca6dc4f481d3898fdd3158c + languageName: node + linkType: hard + +"rimraf@npm:^3.0.2": + version: 3.0.2 + resolution: "rimraf@npm:3.0.2" + dependencies: + glob: ^7.1.3 + bin: + rimraf: bin.js + checksum: 87f4164e396f0171b0a3386cc1877a817f572148ee13a7e113b238e48e8a9f2f31d009a92ec38a591ff1567d9662c6b67fd8818a2dbbaed74bc26a87a2a4a9a0 + languageName: node + linkType: hard + +"safe-buffer@npm:^5.0.1, safe-buffer@npm:~5.1.1": + version: 5.1.2 + resolution: "safe-buffer@npm:5.1.2" + checksum: f2f1f7943ca44a594893a852894055cf619c1fbcb611237fc39e461ae751187e7baf4dc391a72125e0ac4fb2d8c5c0b3c71529622e6a58f46b960211e704903c + languageName: node + linkType: hard + +"safe-buffer@npm:~5.2.0": + version: 5.2.1 + resolution: "safe-buffer@npm:5.2.1" + checksum: b99c4b41fdd67a6aaf280fcd05e9ffb0813654894223afb78a31f14a19ad220bba8aba1cb14eddce1fcfb037155fe6de4e861784eb434f7d11ed58d1e70dd491 + languageName: node + linkType: hard + +"safer-buffer@npm:>= 2.1.2 < 3.0.0": + version: 2.1.2 + resolution: "safer-buffer@npm:2.1.2" + checksum: cab8f25ae6f1434abee8d80023d7e72b598cf1327164ddab31003c51215526801e40b66c5e65d658a0af1e9d6478cadcb4c745f4bd6751f97d8644786c0978b0 + languageName: node + linkType: hard + +"scheduler@npm:^0.23.0": + version: 0.23.0 + resolution: "scheduler@npm:0.23.0" + dependencies: + loose-envify: ^1.1.0 + checksum: d79192eeaa12abef860c195ea45d37cbf2bbf5f66e3c4dcd16f54a7da53b17788a70d109ee3d3dde1a0fd50e6a8fc171f4300356c5aee4fc0171de526bf35f8a + languageName: node + linkType: hard + +"semver@npm:^5.7.0, semver@npm:^5.7.1": + version: 5.7.1 + resolution: "semver@npm:5.7.1" + bin: + semver: ./bin/semver + checksum: 57fd0acfd0bac382ee87cd52cd0aaa5af086a7dc8d60379dfe65fea491fb2489b6016400813930ecd61fd0952dae75c115287a1b16c234b1550887117744dfaf + languageName: node + linkType: hard + +"semver@npm:^6.1.1, semver@npm:^6.1.2, semver@npm:^6.3.0": + version: 6.3.0 + resolution: "semver@npm:6.3.0" + bin: + semver: ./bin/semver.js + checksum: 1b26ecf6db9e8292dd90df4e781d91875c0dcc1b1909e70f5d12959a23c7eebb8f01ea581c00783bbee72ceeaad9505797c381756326073850dc36ed284b21b9 + languageName: node + linkType: hard + +"semver@npm:^7.3.5": + version: 7.3.8 + resolution: "semver@npm:7.3.8" + dependencies: + lru-cache: ^6.0.0 + bin: + semver: bin/semver.js + checksum: ba9c7cbbf2b7884696523450a61fee1a09930d888b7a8d7579025ad93d459b2d1949ee5bbfeb188b2be5f4ac163544c5e98491ad6152df34154feebc2cc337c1 + languageName: node + linkType: hard + +"set-blocking@npm:^2.0.0": + version: 2.0.0 + resolution: "set-blocking@npm:2.0.0" + checksum: 6e65a05f7cf7ebdf8b7c75b101e18c0b7e3dff4940d480efed8aad3a36a4005140b660fa1d804cb8bce911cac290441dc728084a30504d3516ac2ff7ad607b02 + languageName: node + linkType: hard + +"signal-exit@npm:^3.0.7": + version: 3.0.7 + resolution: "signal-exit@npm:3.0.7" + checksum: a2f098f247adc367dffc27845853e9959b9e88b01cb301658cfe4194352d8d2bb32e18467c786a7fe15f1d44b233ea35633d076d5e737870b7139949d1ab6318 + languageName: node + linkType: hard + +"smart-buffer@npm:^4.2.0": + version: 4.2.0 + resolution: "smart-buffer@npm:4.2.0" + checksum: b5167a7142c1da704c0e3af85c402002b597081dd9575031a90b4f229ca5678e9a36e8a374f1814c8156a725d17008ae3bde63b92f9cfd132526379e580bec8b + languageName: node + linkType: hard + +"socks-proxy-agent@npm:^7.0.0": + version: 7.0.0 + resolution: "socks-proxy-agent@npm:7.0.0" + dependencies: + agent-base: ^6.0.2 + debug: ^4.3.3 + socks: ^2.6.2 + checksum: 720554370154cbc979e2e9ce6a6ec6ced205d02757d8f5d93fe95adae454fc187a5cbfc6b022afab850a5ce9b4c7d73e0f98e381879cf45f66317a4895953846 + languageName: node + linkType: hard + +"socks@npm:^2.6.2": + version: 2.7.1 + resolution: "socks@npm:2.7.1" + dependencies: + ip: ^2.0.0 + smart-buffer: ^4.2.0 + checksum: 259d9e3e8e1c9809a7f5c32238c3d4d2a36b39b83851d0f573bfde5f21c4b1288417ce1af06af1452569cd1eb0841169afd4998f0e04ba04656f6b7f0e46d748 + languageName: node + linkType: hard + +"source-map-support@npm:~0.5.20": + version: 0.5.21 + resolution: "source-map-support@npm:0.5.21" + dependencies: + buffer-from: ^1.0.0 + source-map: ^0.6.0 + checksum: 43e98d700d79af1d36f859bdb7318e601dfc918c7ba2e98456118ebc4c4872b327773e5a1df09b0524e9e5063bb18f0934538eace60cca2710d1fa687645d137 + languageName: node + linkType: hard + +"source-map@npm:^0.6.0, source-map@npm:^0.6.1": + version: 0.6.1 + resolution: "source-map@npm:0.6.1" + checksum: 59ce8640cf3f3124f64ac289012c2b8bd377c238e316fb323ea22fbfe83da07d81e000071d7242cad7a23cd91c7de98e4df8830ec3f133cb6133a5f6e9f67bc2 + languageName: node + linkType: hard + +"srcset@npm:4": + version: 4.0.0 + resolution: "srcset@npm:4.0.0" + checksum: aceb898c9281101ef43bfbf96bf04dfae828e1bf942a45df6fad74ae9f8f0a425f4bca1480e0d22879beb40dd2bc6947e0e1e5f4d307a714666196164bc5769d + languageName: node + linkType: hard + +"ssri@npm:^9.0.0": + version: 9.0.1 + resolution: "ssri@npm:9.0.1" + dependencies: + minipass: ^3.1.1 + checksum: fb58f5e46b6923ae67b87ad5ef1c5ab6d427a17db0bead84570c2df3cd50b4ceb880ebdba2d60726588272890bae842a744e1ecce5bd2a2a582fccd5068309eb + languageName: node + linkType: hard + +"stable@npm:^0.1.8": + version: 0.1.8 + resolution: "stable@npm:0.1.8" + checksum: 2ff482bb100285d16dd75cd8f7c60ab652570e8952c0bfa91828a2b5f646a0ff533f14596ea4eabd48bb7f4aeea408dce8f8515812b975d958a4cc4fa6b9dfeb + languageName: node + linkType: hard + +"string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.2.3": + version: 4.2.3 + resolution: "string-width@npm:4.2.3" + dependencies: + emoji-regex: ^8.0.0 + is-fullwidth-code-point: ^3.0.0 + strip-ansi: ^6.0.1 + checksum: e52c10dc3fbfcd6c3a15f159f54a90024241d0f149cf8aed2982a2d801d2e64df0bf1dc351cf8e95c3319323f9f220c16e740b06faecd53e2462df1d2b5443fb + languageName: node + linkType: hard + +"string_decoder@npm:^1.1.1": + version: 1.3.0 + resolution: "string_decoder@npm:1.3.0" + dependencies: + safe-buffer: ~5.2.0 + checksum: 8417646695a66e73aefc4420eb3b84cc9ffd89572861fe004e6aeb13c7bc00e2f616247505d2dbbef24247c372f70268f594af7126f43548565c68c117bdeb56 + languageName: node + linkType: hard + +"strip-ansi@npm:^6.0.1": + version: 6.0.1 + resolution: "strip-ansi@npm:6.0.1" + dependencies: + ansi-regex: ^5.0.1 + checksum: f3cd25890aef3ba6e1a74e20896c21a46f482e93df4a06567cebf2b57edabb15133f1f94e57434e0a958d61186087b1008e89c94875d019910a213181a14fc8c + languageName: node + linkType: hard + +"supports-color@npm:^5.3.0": + version: 5.5.0 + resolution: "supports-color@npm:5.5.0" + dependencies: + has-flag: ^3.0.0 + checksum: 95f6f4ba5afdf92f495b5a912d4abee8dcba766ae719b975c56c084f5004845f6f5a5f7769f52d53f40e21952a6d87411bafe34af4a01e65f9926002e38e1dac + languageName: node + linkType: hard + +"supports-color@npm:^7.1.0": + version: 7.2.0 + resolution: "supports-color@npm:7.2.0" + dependencies: + has-flag: ^4.0.0 + checksum: 3dda818de06ebbe5b9653e07842d9479f3555ebc77e9a0280caf5a14fb877ffee9ed57007c3b78f5a6324b8dbeec648d9e97a24e2ed9fdb81ddc69ea07100f4a + languageName: node + linkType: hard + +"supports-preserve-symlinks-flag@npm:^1.0.0": + version: 1.0.0 + resolution: "supports-preserve-symlinks-flag@npm:1.0.0" + checksum: 53b1e247e68e05db7b3808b99b892bd36fb096e6fba213a06da7fab22045e97597db425c724f2bbd6c99a3c295e1e73f3e4de78592289f38431049e1277ca0ae + languageName: node + linkType: hard + +"svgo@npm:^2.4.0": + version: 2.8.0 + resolution: "svgo@npm:2.8.0" + dependencies: + "@trysound/sax": 0.2.0 + commander: ^7.2.0 + css-select: ^4.1.3 + css-tree: ^1.1.3 + csso: ^4.2.0 + picocolors: ^1.0.0 + stable: ^0.1.8 + bin: + svgo: bin/svgo + checksum: b92f71a8541468ffd0b81b8cdb36b1e242eea320bf3c1a9b2c8809945853e9d8c80c19744267eb91cabf06ae9d5fff3592d677df85a31be4ed59ff78534fa420 + languageName: node + linkType: hard + +"tar@npm:^6.1.11, tar@npm:^6.1.2": + version: 6.1.13 + resolution: "tar@npm:6.1.13" + dependencies: + chownr: ^2.0.0 + fs-minipass: ^2.0.0 + minipass: ^4.0.0 + minizlib: ^2.1.1 + mkdirp: ^1.0.3 + yallist: ^4.0.0 + checksum: 8a278bed123aa9f53549b256a36b719e317c8b96fe86a63406f3c62887f78267cea9b22dc6f7007009738509800d4a4dccc444abd71d762287c90f35b002eb1c + languageName: node + linkType: hard + +"term-size@npm:^2.2.1": + version: 2.2.1 + resolution: "term-size@npm:2.2.1" + checksum: 1ed981335483babc1e8206f843e06bd2bf89b85f0bf5a9a9d928033a0fcacdba183c03ba7d91814643015543ba002f1339f7112402a21da8f24b6c56b062a5a9 + languageName: node + linkType: hard + +"terser@npm:^5.2.0": + version: 5.14.1 + resolution: "terser@npm:5.14.1" + dependencies: + "@jridgewell/source-map": ^0.3.2 + acorn: ^8.5.0 + commander: ^2.20.0 + source-map-support: ~0.5.20 + bin: + terser: bin/terser + checksum: 7b0e51f3d193a11cad82f07e3b0c1d62122eec786f809bdf2a54b865aaa1450872c3a7b6c33b5a40e264834060ffc1d4e197f971a76da5b0137997d756eb7548 + languageName: node + linkType: hard + +"timsort@npm:^0.3.0": + version: 0.3.0 + resolution: "timsort@npm:0.3.0" + checksum: 1a66cb897dacabd7dd7c91b7e2301498ca9e224de2edb9e42d19f5b17c4b6dc62a8d4cbc64f28be82aaf1541cb5a78ab49aa818f42a2989ebe049a64af731e2a + languageName: node + linkType: hard + +"tiny-invariant@npm:^1.0.2": + version: 1.1.0 + resolution: "tiny-invariant@npm:1.1.0" + checksum: 27d29bbb9e1d1d86e25766711c28ad91af6d67c87d561167077ac7fbce5212b97bbfe875e70bc369808e075748c825864c9b61f0e9f8652275ec86bcf4dcc924 + languageName: node + linkType: hard + +"tiny-warning@npm:^1.0.0": + version: 1.0.3 + resolution: "tiny-warning@npm:1.0.3" + checksum: da62c4acac565902f0624b123eed6dd3509bc9a8d30c06e017104bedcf5d35810da8ff72864400ad19c5c7806fc0a8323c68baf3e326af7cb7d969f846100d71 + languageName: node + linkType: hard + +"to-fast-properties@npm:^2.0.0": + version: 2.0.0 + resolution: "to-fast-properties@npm:2.0.0" + checksum: be2de62fe58ead94e3e592680052683b1ec986c72d589e7b21e5697f8744cdbf48c266fa72f6c15932894c10187b5f54573a3bcf7da0bfd964d5caf23d436168 + languageName: node + linkType: hard + +"to-regex-range@npm:^5.0.1": + version: 5.0.1 + resolution: "to-regex-range@npm:5.0.1" + dependencies: + is-number: ^7.0.0 + checksum: f76fa01b3d5be85db6a2a143e24df9f60dd047d151062d0ba3df62953f2f697b16fe5dad9b0ac6191c7efc7b1d9dcaa4b768174b7b29da89d4428e64bc0a20ed + languageName: node + linkType: hard + +"tslib@npm:^2.4.0": + version: 2.4.0 + resolution: "tslib@npm:2.4.0" + checksum: 8c4aa6a3c5a754bf76aefc38026134180c053b7bd2f81338cb5e5ebf96fefa0f417bff221592bf801077f5bf990562f6264fecbc42cd3309b33872cb6fc3b113 + languageName: node + linkType: hard + +"type-fest@npm:^0.20.2": + version: 0.20.2 + resolution: "type-fest@npm:0.20.2" + checksum: 4fb3272df21ad1c552486f8a2f8e115c09a521ad7a8db3d56d53718d0c907b62c6e9141ba5f584af3f6830d0872c521357e512381f24f7c44acae583ad517d73 + languageName: node + linkType: hard + +"unicode-canonical-property-names-ecmascript@npm:^2.0.0": + version: 2.0.0 + resolution: "unicode-canonical-property-names-ecmascript@npm:2.0.0" + checksum: 39be078afd014c14dcd957a7a46a60061bc37c4508ba146517f85f60361acf4c7539552645ece25de840e17e293baa5556268d091ca6762747fdd0c705001a45 + languageName: node + linkType: hard + +"unicode-match-property-ecmascript@npm:^2.0.0": + version: 2.0.0 + resolution: "unicode-match-property-ecmascript@npm:2.0.0" + dependencies: + unicode-canonical-property-names-ecmascript: ^2.0.0 + unicode-property-aliases-ecmascript: ^2.0.0 + checksum: 1f34a7434a23df4885b5890ac36c5b2161a809887000be560f56ad4b11126d433c0c1c39baf1016bdabed4ec54829a6190ee37aa24919aa116dc1a5a8a62965a + languageName: node + linkType: hard + +"unicode-match-property-value-ecmascript@npm:^2.0.0": + version: 2.0.0 + resolution: "unicode-match-property-value-ecmascript@npm:2.0.0" + checksum: 8fe6a09d9085a625cabcead5d95bdbc1a2d5d481712856092ce0347231e81a60b93a68f1b69e82b3076a07e415a72c708044efa2aa40ae23e2e7b5c99ed4a9ea + languageName: node + linkType: hard + +"unicode-match-property-value-ecmascript@npm:^2.1.0": + version: 2.1.0 + resolution: "unicode-match-property-value-ecmascript@npm:2.1.0" + checksum: 8d6f5f586b9ce1ed0e84a37df6b42fdba1317a05b5df0c249962bd5da89528771e2d149837cad11aa26bcb84c35355cb9f58a10c3d41fa3b899181ece6c85220 + languageName: node + linkType: hard + +"unicode-property-aliases-ecmascript@npm:^2.0.0": + version: 2.0.0 + resolution: "unicode-property-aliases-ecmascript@npm:2.0.0" + checksum: dda4d39128cbbede2ac60fbb85493d979ec65913b8a486bf7cb7a375a2346fa48cbf9dc6f1ae23376e7e8e684c2b411434891e151e865a661b40a85407db51d0 + languageName: node + linkType: hard + +"unique-filename@npm:^2.0.0": + version: 2.0.1 + resolution: "unique-filename@npm:2.0.1" + dependencies: + unique-slug: ^3.0.0 + checksum: 807acf3381aff319086b64dc7125a9a37c09c44af7620bd4f7f3247fcd5565660ac12d8b80534dcbfd067e6fe88a67e621386dd796a8af828d1337a8420a255f + languageName: node + linkType: hard + +"unique-slug@npm:^3.0.0": + version: 3.0.0 + resolution: "unique-slug@npm:3.0.0" + dependencies: + imurmurhash: ^0.1.4 + checksum: 49f8d915ba7f0101801b922062ee46b7953256c93ceca74303bd8e6413ae10aa7e8216556b54dc5382895e8221d04f1efaf75f945c2e4a515b4139f77aa6640c + languageName: node + linkType: hard + +"update-browserslist-db@npm:^1.0.10": + version: 1.0.10 + resolution: "update-browserslist-db@npm:1.0.10" + dependencies: + escalade: ^3.1.1 + picocolors: ^1.0.0 + peerDependencies: + browserslist: ">= 4.21.0" + bin: + browserslist-lint: cli.js + checksum: 12db73b4f63029ac407b153732e7cd69a1ea8206c9100b482b7d12859cd3cd0bc59c602d7ae31e652706189f1acb90d42c53ab24a5ba563ed13aebdddc5561a0 + languageName: node + linkType: hard + +"util-deprecate@npm:^1.0.1": + version: 1.0.2 + resolution: "util-deprecate@npm:1.0.2" + checksum: 474acf1146cb2701fe3b074892217553dfcf9a031280919ba1b8d651a068c9b15d863b7303cb15bd00a862b498e6cf4ad7b4a08fb134edd5a6f7641681cb54a2 + languageName: node + linkType: hard + +"utility-types@npm:^3.10.0": + version: 3.10.0 + resolution: "utility-types@npm:3.10.0" + checksum: 8f274415c6196ab62883b8bd98c9d2f8829b58016e4269aaa1ebd84184ac5dda7dc2ca45800c0d5e0e0650966ba063bf9a412aaeaea6850ca4440a391283d5c8 + languageName: node + linkType: hard + +"v8-compile-cache@npm:^2.0.0": + version: 2.0.2 + resolution: "v8-compile-cache@npm:2.0.2" + checksum: 22e7f6464845c716b61ae00e05e2292e0d7790fb25ad9bf90b0c3b01a5088bbb6509c3c8fddcfab40b92f65e95d2cb50a71df8515d9ab3118c6931a590dc77b1 + languageName: node + linkType: hard + +"value-equal@npm:^1.0.1": + version: 1.0.1 + resolution: "value-equal@npm:1.0.1" + checksum: bb7ae1facc76b5cf8071aeb6c13d284d023fdb370478d10a5d64508e0e6e53bb459c4bbe34258df29d82e6f561f874f0105eba38de0e61fe9edd0bdce07a77a2 + languageName: node + linkType: hard + +"warning@npm:^4.0.2": + version: 4.0.3 + resolution: "warning@npm:4.0.3" + dependencies: + loose-envify: ^1.0.0 + checksum: 4f2cb6a9575e4faf71ddad9ad1ae7a00d0a75d24521c193fa464f30e6b04027bd97aa5d9546b0e13d3a150ab402eda216d59c1d0f2d6ca60124d96cd40dfa35c + languageName: node + linkType: hard + +"weak-lru-cache@npm:^1.2.2": + version: 1.2.2 + resolution: "weak-lru-cache@npm:1.2.2" + checksum: 0fbe16839d193ed82ddb4fe331ca8cfaee2ecbd42596aa02366c708956cf41f7258f2d5411c3bc9aa099c26058dc47afbd2593d449718a18e4ef4d870c5ace18 + languageName: node + linkType: hard + +"webui@workspace:.": + version: 0.0.0-use.local + resolution: "webui@workspace:." + dependencies: + "@babel/core": ^7.21.3 + "@babel/preset-env": ^7.20.2 + "@babel/preset-react": ^7.18.6 + "@parcel/css-linux-arm64-gnu": ^1.13.1 + bootstrap: ^5.2.3 + bootswatch: ^5.2.3 + classnames: ^2.3.2 + dygraphs: ^2.2.1 + feather-icons: ^4.29.0 + font-awesome: ^4.7.0 + jquery: ^3.6.4 + lodash: ^4.17.21 + memoize-one: ^6.0.0 + parcel: ^2.8.3 + popper.js: ^1.16.1 + process: ^0.11.10 + prop-types: ^15.8.1 + react: ^18.2.0 + react-ace: ^10.1.0 + react-contextmenu: ^2.14.0 + react-dom: ^18.2.0 + react-router-dom: ^5.3.4 + react-table: ^6.11.5 + reactdom: ^2.0.0 + reactstrap: ^9.1.6 + dependenciesMeta: + "@parcel/css-linux-arm64-gnu": + optional: true + languageName: unknown + linkType: soft + +"which@npm:^2.0.2": + version: 2.0.2 + resolution: "which@npm:2.0.2" + dependencies: + isexe: ^2.0.0 + bin: + node-which: ./bin/node-which + checksum: 1a5c563d3c1b52d5f893c8b61afe11abc3bab4afac492e8da5bde69d550de701cf9806235f20a47b5c8fa8a1d6a9135841de2596535e998027a54589000e66d1 + languageName: node + linkType: hard + +"wide-align@npm:^1.1.5": + version: 1.1.5 + resolution: "wide-align@npm:1.1.5" + dependencies: + string-width: ^1.0.2 || 2 || 3 || 4 + checksum: d5fc37cd561f9daee3c80e03b92ed3e84d80dde3365a8767263d03dacfc8fa06b065ffe1df00d8c2a09f731482fcacae745abfbb478d4af36d0a891fad4834d3 + languageName: node + linkType: hard + +"wrappy@npm:1": + version: 1.0.2 + resolution: "wrappy@npm:1.0.2" + checksum: 159da4805f7e84a3d003d8841557196034155008f817172d4e986bd591f74aa82aa7db55929a54222309e01079a65a92a9e6414da5a6aa4b01ee44a511ac3ee5 + languageName: node + linkType: hard + +"xxhash-wasm@npm:^0.4.2": + version: 0.4.2 + resolution: "xxhash-wasm@npm:0.4.2" + checksum: 747b32fcfed1dc9a1e7592b134e4e65794bc10fd5d32515792e486bf4d0b65f9dec790cfc49ce2f9c01dd02e3593c3a6cd51df1ef37adf003c5bbd386c43c64d + languageName: node + linkType: hard + +"yallist@npm:^3.0.2": + version: 3.1.1 + resolution: "yallist@npm:3.1.1" + checksum: 48f7bb00dc19fc635a13a39fe547f527b10c9290e7b3e836b9a8f1ca04d4d342e85714416b3c2ab74949c9c66f9cebb0473e6bc353b79035356103b47641285d + languageName: node + linkType: hard + +"yallist@npm:^4.0.0": + version: 4.0.0 + resolution: "yallist@npm:4.0.0" + checksum: 343617202af32df2a15a3be36a5a8c0c8545208f3d3dfbc6bb7c3e3b7e8c6f8e7485432e4f3b88da3031a6e20afa7c711eded32ddfb122896ac5d914e75848d5 + languageName: node + linkType: hard + +"yaml@npm:^1.10.0": + version: 1.10.2 + resolution: "yaml@npm:1.10.2" + checksum: ce4ada136e8a78a0b08dc10b4b900936912d15de59905b2bf415b4d33c63df1d555d23acb2a41b23cf9fb5da41c256441afca3d6509de7247daa062fd2c5ea5f + languageName: node + linkType: hard