diff --git a/.gitignore b/.gitignore index 113d56e2..aaec7a35 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,6 @@ **/dot.dot **/flamegraph.svg **/.swp +.vscode +.env +notes.md \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index bd01078d..431407f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "ahash" version = "0.8.10" @@ -32,6 +47,7 @@ dependencies = [ "graph", "shared", "solang-parser", + "tracing", ] [[package]] @@ -42,23 +58,24 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.4" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.4" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anstyle-parse" @@ -90,11 +107,12 @@ dependencies = [ [[package]] name = "ariadne" -version = "0.2.0" -source = "git+https://github.com/brockelmore/ariadne#c2e3a8e79f369e8a785e1ca337e144ebc946f115" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44055e597c674aef7cb903b2b9f6e4cba1277ed0d2d61dae7cd52d7ffa81f8e2" dependencies = [ "unicode-width", - "yansi", + "yansi 1.0.1", ] [[package]] @@ -112,6 +130,12 @@ dependencies = [ "term", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "atty" version = "0.2.14" @@ -141,12 +165,33 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base16ct" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "base64ct" version = "1.6.0" @@ -234,6 +279,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" +[[package]] +name = "cc" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" + [[package]] name = "cfg-if" version = "1.0.0" @@ -348,7 +399,9 @@ dependencies = [ "graph", "petgraph", "pyrometer", + "reqwest", "shared", + "tokio", "tracing", "tracing-subscriber", "tracing-tree", @@ -379,6 +432,22 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + [[package]] name = "cpufeatures" version = "0.2.11" @@ -593,6 +662,15 @@ dependencies = [ "log", ] +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +dependencies = [ + "cfg-if", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -718,12 +796,81 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + [[package]] name = "funty" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -746,6 +893,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" + [[package]] name = "graph" version = "0.2.0" @@ -773,6 +926,25 @@ dependencies = [ "subtle", ] +[[package]] +name = "h2" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap 2.1.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "half" version = "1.8.2" @@ -827,6 +999,112 @@ dependencies = [ "digest", ] +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "hyper" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b875924a60b96e5d7b9ae7b066540b1dd1cbd90d1828f54c92e02a283351c56" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "socket2", + "tokio", + "tower", + "tower-service", + "tracing", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "impl-codec" version = "0.6.0" @@ -885,6 +1163,12 @@ dependencies = [ "hashbrown 0.14.3", ] +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + [[package]] name = "is-terminal" version = "0.4.9" @@ -896,6 +1180,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + [[package]] name = "itertools" version = "0.10.5" @@ -1030,9 +1320,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "matchers" @@ -1058,6 +1348,49 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "new_debug_unreachable" version = "1.0.4" @@ -1093,6 +1426,16 @@ dependencies = [ "libm", ] +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.3", + "libc", +] + [[package]] name = "num_enum" version = "0.7.1" @@ -1114,6 +1457,15 @@ dependencies = [ "syn 2.0.39", ] +[[package]] +name = "object" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.19.0" @@ -1151,6 +1503,50 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "openssl" +version = "0.10.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +dependencies = [ + "bitflags 2.4.1", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "os_str_bytes" version = "6.6.1" @@ -1212,6 +1608,12 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + [[package]] name = "petgraph" version = "0.6.4" @@ -1273,12 +1675,38 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + [[package]] name = "pin-project-lite" version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "pkcs8" version = "0.10.2" @@ -1289,6 +1717,12 @@ dependencies = [ "spki", ] +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + [[package]] name = "plotters" version = "0.3.5" @@ -1336,7 +1770,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" dependencies = [ "diff", - "yansi", + "yansi 0.5.1", ] [[package]] @@ -1433,10 +1867,13 @@ dependencies = [ "ethers-core", "graph", "petgraph", + "reqwest", + "serde", "serde_json", "shared", "solang-parser", "solc-expressions", + "tokio", "tracing", "tracing-subscriber", ] @@ -1593,6 +2030,48 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "reqwest" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + [[package]] name = "rfc6979" version = "0.4.0" @@ -1625,6 +2104,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + [[package]] name = "rustc-hex" version = "2.1.0" @@ -1644,6 +2129,22 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustls-pemfile" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +dependencies = [ + "base64", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" + [[package]] name = "rustversion" version = "1.0.14" @@ -1689,6 +2190,15 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -1709,6 +2219,29 @@ dependencies = [ "zeroize", ] +[[package]] +name = "security-framework" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "serde" version = "1.0.193" @@ -1740,6 +2273,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "sha2" version = "0.10.8" @@ -1778,11 +2323,23 @@ dependencies = [ "ethers-core", "hex", "petgraph", + "reqwest", + "serde", "solang-parser", + "tokio", "tracing", "tracing-subscriber", ] +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + [[package]] name = "signature" version = "2.2.0" @@ -1799,11 +2356,30 @@ version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + [[package]] name = "smallvec" -version = "1.11.2" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] [[package]] name = "solang-parser" @@ -1921,6 +2497,33 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tap" version = "1.0.1" @@ -2006,6 +2609,74 @@ dependencies = [ "serde_json", ] +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + [[package]] name = "toml_datetime" version = "0.6.3" @@ -2034,6 +2705,33 @@ dependencies = [ "winnow", ] +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + [[package]] name = "tracing" version = "0.1.40" @@ -2107,6 +2805,12 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "typenum" version = "1.17.0" @@ -2131,12 +2835,27 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-width" version = "0.1.11" @@ -2149,6 +2868,17 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + [[package]] name = "utf8parse" version = "0.2.1" @@ -2161,6 +2891,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.4" @@ -2177,6 +2913,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -2208,6 +2953,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.89" @@ -2419,6 +3176,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "wyz" version = "0.5.1" @@ -2434,6 +3201,12 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + [[package]] name = "zerocopy" version = "0.7.32" diff --git a/Cargo.toml b/Cargo.toml index 5bc8a535..e3e5e410 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,7 +52,7 @@ tracing-subscriber = { version = "0.3", features = [ tracing-tree = "0.3.0" ethers-core = "*" hex = "0.4.3" -ariadne = "0.2.0" +ariadne = "0.4.1" petgraph = "0.6.2" ahash = "0.8.10" @@ -61,10 +61,6 @@ ahash = "0.8.10" # [workspace] # members = ["cli", "shared"] -# we patch ariadne to allow for counting by bytes because solang uses byte locations not char locations -[patch.crates-io] -ariadne = { git = "https://github.com/brockelmore/ariadne" } - # ###################################### # # Benchmarks # ###################################### diff --git a/crates/analyzers/Cargo.toml b/crates/analyzers/Cargo.toml index 136ace93..a715b698 100644 --- a/crates/analyzers/Cargo.toml +++ b/crates/analyzers/Cargo.toml @@ -13,6 +13,7 @@ repository.workspace = true [dependencies] graph.workspace = true shared.workspace = true +tracing.workspace = true solang-parser.workspace = true ariadne.workspace = true \ No newline at end of file diff --git a/crates/analyzers/src/func_analyzer/mod.rs b/crates/analyzers/src/func_analyzer/mod.rs index dc3c5bcf..0eb3b0e3 100644 --- a/crates/analyzers/src/func_analyzer/mod.rs +++ b/crates/analyzers/src/func_analyzer/mod.rs @@ -114,6 +114,7 @@ impl<'a> FunctionVarsBoundAnalysis { .with_cross_gap(false) .with_underlines(true) .with_tab_width(4) + .with_index_type(ariadne::IndexType::Byte) .with_multiline_arrows(false), ); diff --git a/crates/analyzers/src/func_analyzer/report_display.rs b/crates/analyzers/src/func_analyzer/report_display.rs index be106ff4..828d8c9f 100644 --- a/crates/analyzers/src/func_analyzer/report_display.rs +++ b/crates/analyzers/src/func_analyzer/report_display.rs @@ -66,6 +66,7 @@ impl<'a> ReportDisplay for CLIFunctionVarsBoundAnalysis<'a> { Config::default() .with_cross_gap(false) .with_underlines(true) + .with_index_type(ariadne::IndexType::Byte) .with_tab_width(4), ); diff --git a/crates/analyzers/src/var_analyzer/report_display.rs b/crates/analyzers/src/var_analyzer/report_display.rs index a4b1e48a..403454bb 100644 --- a/crates/analyzers/src/var_analyzer/report_display.rs +++ b/crates/analyzers/src/var_analyzer/report_display.rs @@ -76,6 +76,7 @@ impl ReportDisplay for VarBoundAnalysis { Config::default() .with_cross_gap(false) .with_underlines(true) + .with_index_type(ariadne::IndexType::Byte) .with_tab_width(4), ); diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 5029e551..4b76ced0 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -16,7 +16,6 @@ analyzers.workspace = true shared.workspace = true pyrometer.workspace = true graph.workspace = true -# queries.workspace = true ariadne.workspace = true tracing.workspace = true @@ -25,8 +24,9 @@ tracing-tree.workspace = true petgraph.workspace = true ethers-core.workspace = true - clap = { version = "4.1.4", features = ["derive"] } +tokio = { version = "1", features = ["full"] } +reqwest = { version = "0.12", features = ["json"] } [[bin]] name = "pyrometer" diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index a55d8426..b4761afe 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -5,12 +5,14 @@ use graph::{ Edge, }; use pyrometer::{Analyzer, Root, SourcePath}; -use shared::GraphDot; -use shared::Search; +use reqwest::Client; +use shared::{post_to_site, Search}; +use shared::{GraphDot, USE_DEBUG_SITE}; use ariadne::sources; use clap::{ArgAction, Parser, ValueHint}; +use tracing::{debug, error, info, trace, warn}; use tracing_subscriber::{prelude::*, Registry}; use std::{ @@ -19,6 +21,7 @@ use std::{ fs, path::PathBuf, }; +use tokio::runtime::Runtime; #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] @@ -100,6 +103,10 @@ struct Args { /// Print stats about the IR #[clap(long)] pub stats: bool, + + /// Post pyrometer debugging information to debugging site + #[clap(long)] + pub debug_site: bool, } pub fn subscriber() { @@ -114,11 +121,12 @@ pub fn tree_subscriber() { .with( tracing_tree::HierarchicalLayer::default() .with_indent_lines(true) - .with_indent_amount(2) - .with_thread_names(true), // .with_thread_ids(true) - // .with_verbose_exit(true) - // .with_verbose_entry(true) - // .with_targets(true) + .with_indent_amount(1) + // .with_targets(true) + .with_thread_names(false), // .with_thread_ids(true) + // .with_verbose_exit(true) + // .with_verbose_entry(true) + // .with_targets(true) ) .with(tracing_subscriber::filter::EnvFilter::from_default_env()); tracing::subscriber::set_global_default(subscriber).unwrap(); @@ -226,6 +234,7 @@ fn main() { show_nonreverts: args.show_nonreverts.unwrap_or(true), }, }; + let mut analyzer = Analyzer { max_depth: args.max_stack_depth, root: Root::RemappingsDirectory(env::current_dir().unwrap()), @@ -257,6 +266,30 @@ fn main() { let mut arena_base = Default::default(); let arena = &mut arena_base; + + if args.debug_site { + unsafe { + USE_DEBUG_SITE = true; + } + + let rt = Runtime::new().unwrap(); + rt.block_on(async { + let client = Client::new(); + let res = client + .post("http://127.0.0.1:8545/clear") + .send() + .await + .expect("Failed to send request"); + + if res.status().is_success() { + trace!("Successfully cleared history of site"); + } else { + error!("Failed to clear history of site: {:?}", res.status()); + } + }); + post_to_site(&analyzer, arena); + } + let t0 = std::time::Instant::now(); let maybe_entry = analyzer.parse(arena, &sol, ¤t_path, true); let t_end = t0.elapsed(); @@ -264,6 +297,27 @@ fn main() { println!("DONE ANALYZING IN: {parse_time}ms. Writing to cli..."); + // println!("Arena: {:#?}", analyzer.range_arena); + if unsafe { USE_DEBUG_SITE } { + use pyrometer::graph_backend::mermaid_str; + use pyrometer::graph_backend::post_to_site_arena; + use pyrometer::graph_backend::Elems; + let elems = Elems::try_from(&*arena); + match elems { + Ok(elems) => { + let elems_graph = elems.to_graph(&analyzer, arena); + let elems_graph_mermaid_str = mermaid_str(&elems_graph); + post_to_site_arena(elems_graph_mermaid_str); + } + Err(e) => { + eprintln!("Can't post arena, error creating Elems: {:?}", e); + } + }; + + // post the graph to the site + post_to_site(&analyzer, arena); + } + if args.stats { println!("{}", analyzer.stats(t_end, arena)); } diff --git a/crates/graph/src/graph_elements.rs b/crates/graph/src/graph_elements.rs index 652d643d..5b255e99 100644 --- a/crates/graph/src/graph_elements.rs +++ b/crates/graph/src/graph_elements.rs @@ -1,7 +1,7 @@ use crate::elem::Elem; use crate::{nodes::*, VarType}; -use shared::{AnalyzerLike, GraphLike, Heirarchical, NodeIdx, RangeArena}; +use shared::{AnalyzerLike, GraphDot, GraphLike, Heirarchical, NodeIdx, RangeArena}; use lazy_static::lazy_static; use petgraph::{Directed, Graph}; @@ -9,7 +9,10 @@ use solang_parser::pt::{Identifier, Loc}; use std::collections::HashMap; -pub trait GraphBackend: GraphLike> {} +pub trait GraphBackend: + GraphLike> + GraphDot +{ +} pub trait AnalyzerBackend: AnalyzerLike< Builtin = Builtin, @@ -226,6 +229,7 @@ lazy_static! { m.insert("bg", "#1a1b26"); m.insert("font", "#c0caf5"); m.insert("deepred", "#703440"); + m.insert("deeporange", "#b5683c"); m.insert("default", "#565f89"); m }; @@ -397,9 +401,7 @@ pub enum ContextEdge { } #[derive(Default)] -pub(crate) struct DummyGraph { - pub range_arena: RangeArena>, -} +pub(crate) struct DummyGraph {} impl GraphLike for DummyGraph { type Node = Node; @@ -412,12 +414,47 @@ impl GraphLike for DummyGraph { fn graph(&self) -> &Graph { panic!("Dummy Graph") } - fn range_arena(&self) -> &RangeArena> { - &self.range_arena - } - fn range_arena_mut(&mut self) -> &mut RangeArena> { - &mut self.range_arena - } } impl GraphBackend for DummyGraph {} +impl GraphDot for DummyGraph { + type T = Elem; + + fn dot_str(&self, _arena: &mut RangeArena<::T>) -> String { + // Provide a basic implementation or a placeholder + "digraph DummyGraph {}".to_string() + } + + fn cluster_str( + &self, + _arena: &mut RangeArena, + _node: NodeIdx, + _cluster_num: &mut usize, + _is_killed: bool, + _handled_nodes: std::sync::Arc>>, + _handled_edges: std::sync::Arc< + std::sync::Mutex>>, + >, + _depth: usize, + _as_mermaid: bool, + ) -> Option + where + Self: std::marker::Sized, + { + todo!() + } + + fn dot_str_no_tmps(&self, _arena: &mut RangeArena) -> String + where + Self: std::marker::Sized, + { + todo!() + } + + fn mermaid_str(&self, _arena: &mut RangeArena) -> String + where + Self: std::marker::Sized, + { + todo!() + } +} diff --git a/crates/graph/src/range/elem/elem_enum/impls.rs b/crates/graph/src/range/elem/elem_enum/impls.rs index f9aa87d8..8ef2a405 100644 --- a/crates/graph/src/range/elem/elem_enum/impls.rs +++ b/crates/graph/src/range/elem/elem_enum/impls.rs @@ -57,6 +57,33 @@ impl Elem { _ => None, } } + + pub fn arena_graph_node_label(&self) -> String { + match self { + Elem::Reference(reference) => { + format!("Ref-CVar{}", reference.idx.index()) + } + Elem::ConcreteDyn(range_dyn) => { + format!("concdyn-{}", self) + } + Elem::Concrete(range_concrete) => { + format!("conc-{}", self) + } + Elem::Expr(range_expr) => { + // Unbox and check the lhs and rhs to see if they are arena indices + let lhs_str = range_expr.lhs.arena_graph_node_label(); + let rhs_str = range_expr.rhs.arena_graph_node_label(); + let op = range_expr.op.clone(); + format!("expr-{}", &self) + } + Elem::Arena(arena_idx) => { + format!("Arena({})", arena_idx) + } + Elem::Null => { + todo!() + } + } + } } impl Elem { diff --git a/crates/pyrometer/Cargo.toml b/crates/pyrometer/Cargo.toml index fd35e3e8..7a279ba5 100644 --- a/crates/pyrometer/Cargo.toml +++ b/crates/pyrometer/Cargo.toml @@ -24,6 +24,9 @@ tracing.workspace = true tracing-subscriber.workspace = true ahash.workspace = true serde_json = "1" +tokio = { version = "1", features = ["full"] } +serde = { version = "1", features = ["derive"] } +reqwest = { version = "0.12", features = ["json"] } diff --git a/crates/pyrometer/src/analyzer.rs b/crates/pyrometer/src/analyzer.rs index 8dee588d..943f6966 100644 --- a/crates/pyrometer/src/analyzer.rs +++ b/crates/pyrometer/src/analyzer.rs @@ -1,11 +1,15 @@ use crate::builtin_fns; -use graph::elem::Elem; -use shared::RangeArena; - use analyzers::LocStrSpan; +use graph::elem::Elem; use graph::{nodes::*, ContextEdge, Edge, Node, VarType}; +use reqwest::Client; +use serde::{Deserialize, Serialize}; +use shared::GraphDot; use shared::{AnalyzerLike, GraphLike, JoinStats, NodeIdx, Search}; +use shared::{RangeArena, USE_DEBUG_SITE}; use solc_expressions::{ExprErr, FnCallBuilder, IntoExprErr, StatementParser}; +use tokio::runtime::Runtime; +use tracing::{debug, error, info, trace, warn}; use ahash::AHashMap; use ariadne::{Cache, Color, Config, Fmt, Label, Report, ReportKind, Source, Span}; @@ -160,7 +164,7 @@ impl Default for Analyzer { builtin_fn_inputs: Default::default(), expr_errs: Default::default(), max_depth: 200, - max_width: 2_i32.pow(14) as usize, + max_width: 2_i32.pow(14) as usize, // 14 splits == 16384 contexts parse_fn: NodeIdx::from(0).into(), debug_panic: false, fn_calls_fns: Default::default(), @@ -474,7 +478,8 @@ impl Analyzer { Config::default() .with_cross_gap(false) .with_underlines(true) - .with_tab_width(4), + .with_tab_width(4) + .with_index_type(ariadne::IndexType::Byte), ) .with_label( Label::new(str_span) @@ -497,6 +502,9 @@ impl Analyzer { let file_no = self.file_no; self.sources .push((current_path.clone(), src.to_string(), Some(file_no), None)); + if unsafe { USE_DEBUG_SITE } { + Self::post_source_to_site(file_no, ¤t_path.path_to_solidity_source(), src); + } match solang_parser::parse(src, file_no) { Ok((source_unit, _comments)) => { let parent = @@ -971,7 +979,7 @@ impl Analyzer { let normalized_remapped = normalize_path(remapped.path_to_solidity_source()); // take self.sources entry with the same path as remapped and update the file_no - if let Some((_, _, optional_file_no, _)) = + if let Some((source_path, source, optional_file_no, _)) = self.sources.iter_mut().find(|(path, _, _, _)| { normalize_path(path.path_to_solidity_source()) == normalized_remapped }) @@ -983,6 +991,10 @@ impl Analyzer { self.file_no += 1; let file_no = self.file_no; *optional_file_no = Some(file_no); + if unsafe { USE_DEBUG_SITE } { + // send the source to the site + Self::post_source_to_site(file_no, &source_path.path_to_solidity_source(), source); + } } let maybe_entry = self.parse(arena, &sol, &remapped, false); @@ -1390,6 +1402,42 @@ impl Analyzer { }; ty_node } + + fn post_source_to_site(file_no: usize, path: &PathBuf, source: &str) + where + Self: std::marker::Sized, + Self: AnalyzerLike, + { + let rt = Runtime::new().unwrap(); + rt.block_on(async { + Self::post_source_to_site_async(file_no, path, source).await; + }); + } + + async fn post_source_to_site_async(file_no: usize, path: &PathBuf, source: &str) + where + Self: std::marker::Sized, + Self: AnalyzerLike, + { + let client = Client::new(); + let source_msg = SourceMessage { + file_number: file_no, + path: path.to_path_buf(), + source: source.to_string(), + }; + let res = client + .post("http://127.0.0.1:8545/addsource") + .json(&source_msg) + .send() + .await + .expect("Failed to send request"); + + if res.status().is_success() { + trace!("Successfully posted source to site"); + } else { + error!("Failed to post source to site: {:?}", res.status()); + } + } } /// Print the report of parser's diagnostics @@ -1450,3 +1498,10 @@ pub fn normalize_path>(path: P) -> PathBuf { normalized_path } + +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash)] +struct SourceMessage { + file_number: usize, + path: PathBuf, + source: String, +} diff --git a/crates/pyrometer/src/analyzer_backend.rs b/crates/pyrometer/src/analyzer_backend.rs index b4834d50..b1c5b627 100644 --- a/crates/pyrometer/src/analyzer_backend.rs +++ b/crates/pyrometer/src/analyzer_backend.rs @@ -1,5 +1,4 @@ use crate::Analyzer; - use graph::{ elem::Elem, nodes::{ @@ -276,4 +275,17 @@ impl AnalyzerLike for Analyzer { fn handled_funcs_mut(&mut self) -> &mut Vec { &mut self.handled_funcs } + + fn file_mapping(&self) -> BTreeMap { + let mut file_mapping: BTreeMap = BTreeMap::new(); + for (source_path, _, o_file_no, _) in self.sources.iter() { + if let Some(file_no) = o_file_no { + file_mapping.insert( + *file_no, + source_path.path_to_solidity_source().display().to_string(), + ); + } + } + file_mapping + } } diff --git a/crates/pyrometer/src/graph_backend.rs b/crates/pyrometer/src/graph_backend.rs index 3512cbdd..64ad9537 100644 --- a/crates/pyrometer/src/graph_backend.rs +++ b/crates/pyrometer/src/graph_backend.rs @@ -1,19 +1,24 @@ use crate::Analyzer; -use graph::elem::Elem; +use graph::elem::RangeElem; use graph::nodes::Concrete; -use shared::RangeArena; -// use std::collections::hash_map::DefaultHasher; -// use std::hash::Hash; -// use std::hash::Hasher; - use graph::{ as_dot_str, nodes::ContextNode, AnalyzerBackend, AsDotStr, ContextEdge, Edge, GraphBackend, Node, }; +use graph::{elem::Elem, nodes::ContextVarNode, TOKYO_NIGHT_COLORS}; +use reqwest::Client; +use serde::{Deserialize, Serialize}; +use shared::RangeArena; use shared::{GraphDot, GraphLike, NodeIdx, Search}; +use std::collections::hash_map::DefaultHasher; +use std::hash::Hash; +use std::hash::Hasher; +use std::{collections::HashMap, fmt::Display}; +use tokio::runtime::Runtime; +use tracing::{error, trace, warn}; use petgraph::{dot::Dot, graph::EdgeIndex, visit::EdgeRef, Directed, Direction, Graph}; - +use std::convert::TryFrom; use std::{ collections::BTreeSet, sync::{Arc, Mutex}, @@ -31,28 +36,577 @@ impl GraphLike for Analyzer { &self.graph } - fn range_arena(&self) -> &RangeArena> { - &self.range_arena + fn add_node(&mut self, node: impl Into) -> NodeIdx + where + Self: std::marker::Sized, + Self: GraphLike, + { + let res = self.graph_mut().add_node(node.into()); + res } - fn range_arena_mut(&mut self) -> &mut RangeArena> { - &mut self.range_arena + fn add_edge( + &mut self, + from_node: impl Into, + to_node: impl Into, + edge: impl Into, + ) where + Self: std::marker::Sized, + Self: GraphLike, + { + self.graph_mut() + .add_edge(from_node.into(), to_node.into(), edge.into()); } +} + +pub fn post_to_site_arena(arena_str: String) { + let rt = Runtime::new().unwrap(); + rt.block_on(async { + post_to_site_arena_async(arena_str).await; + }); +} + +async fn post_to_site_arena_async(arena_str: String) { + let client = Client::new(); + let graph_msg = ArenaMessage { + arena: arena_str.to_string(), + }; + + let res = client + .post("http://127.0.0.1:8545/updatearena") + .json(&graph_msg) + .send() + .await + .expect("Failed to send request"); + + if res.status().is_success() { + trace!("Successfully posted arena to site"); + } else { + error!("Failed to post arena to site: {:?}", res.status()); + } +} + +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash)] +struct ArenaMessage { + arena: String, +} + +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash)] +pub enum ArenaNode { + /// Arena node (index) + ARENA(usize), + /// ContextVar node (label is the string representation of the node) + CVAR(String), + /// Elem node (e.g. an expression) (label is the string representation of the node) + ELEM(String), +} + +impl Display for ArenaNode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + // warning: changing these will impact the pyro-debug site when rendering edge styling + let variant_name = match self { + ArenaNode::ARENA(idx) => format!("index {}", idx), + ArenaNode::ELEM(label) => label.to_string(), + ArenaNode::CVAR(label) => label.to_string(), + }; + write!(f, "{}", variant_name) + } +} + +impl ArenaNode { + pub fn dot_str_color(&self) -> String { + let c = match self { + ArenaNode::ARENA(_) => TOKYO_NIGHT_COLORS.get("red1").unwrap(), + ArenaNode::CVAR(_) => TOKYO_NIGHT_COLORS.get("deeporange").unwrap(), + ArenaNode::ELEM(_) => TOKYO_NIGHT_COLORS.get("blue0").unwrap(), + }; + c.to_string() + } +} + +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash)] +pub enum ArenaEdge { + LHS, + RHS, + ARENA, + VAR, + REF, + MIN, + MAX, + NONE, +} + +impl Display for ArenaEdge { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + // warning: changing these will impact the pyro-debug site when rendering edge styling + let variant_name = match self { + ArenaEdge::LHS => "LHS", + ArenaEdge::RHS => "RHS", + ArenaEdge::ARENA => "ARENA", + ArenaEdge::VAR => "VAR", + ArenaEdge::MIN => "MIN", + ArenaEdge::MAX => "MAX", + ArenaEdge::REF => "REF", + ArenaEdge::NONE => "", + }; + write!(f, "{}", variant_name) + } +} + +#[derive(Debug)] +pub enum ElemsError { + BorrowError(String), + MissingMap(String), +} + +impl std::fmt::Display for ElemsError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ElemsError::BorrowError(msg) => write!(f, "BorrowError: {}", msg), + ElemsError::MissingMap(msg) => write!(f, "MissingMap: {}", msg), + } + } +} +impl std::error::Error for ElemsError {} +pub struct Elems { + /// arena_idx, elem + pub inner: Vec<(usize, Elem)>, +} + +impl TryFrom<&RangeArena>> for Elems { + type Error = ElemsError; + fn try_from(arena: &RangeArena>) -> Result { + // Collect the elements and their indices first from ranges + // Collect the elements and their indices first from ranges + let mut inner = Vec::new(); + for elem in &arena.ranges { + // Get the map value + if let Some(map_value) = arena.map.get(&elem).copied() { + // println!("Adding idx {} to elems {}", map_value, elem); + inner.push((map_value, elem.clone())); + } else { + // println!("NONE REF elem: {:?}", elem); + return Err(ElemsError::MissingMap(format!("elem {:?}", elem))); + } + } + + // Search .map for any entries that werent in .ranges + let inner_indices: BTreeSet<_> = inner.iter().map(|(idx, _)| *idx).collect(); + let missing_entries: Vec<_> = arena + .map + .iter() + .filter(|(_, &v)| !inner_indices.contains(&v)) + .collect(); + + { + // Log out missing entries + let missing_entries_str = missing_entries + .iter() + .map(|(idx, elem)| format!("\telem {}: {}", idx, elem)) + .collect::>() + .join("\n"); + warn!( + "`RangeArena.ranges` is missing {} entries from the map:\n{}", + missing_entries.len(), + missing_entries_str + ); + } + + // Add any missing entries to inner + for (_elem, &idx) in missing_entries { + if let Some(range_elem) = arena.ranges.get(idx) { + inner.push((idx, range_elem.clone())); + } + } + + // Sort the collected elements by their indices + inner.sort_by(|a, b| a.0.cmp(&b.0)); + // dedup is needed as there are duplicate indices in the inner vec. TODO @brock is this a bug? arena has duplicate elems + inner.dedup(); + + // Print out elems + // for (idx, elem) in inner.iter() { + // println!("elem {}: {}", idx, elem); + // } - // fn range_arena_idx(&self, elem: &Self::RangeElem) -> Option { - // if let Elem::Arena(idx) = elem { - // Some(*idx) - // } else { - // self.range_arena().map.get(elem).copied() - // } - // } + Ok(Elems { inner }) + } } -// fn calculate_hash(t: &T) -> u64 { -// let mut s = DefaultHasher::new(); -// t.hash(&mut s); -// s.finish() -// } +impl Elems { + /// Convert Elems into a Graph + /// + /// First pass: + /// - create nodes for each arena index + /// - this is needed separately, since when making edges some earlier nodes point to later nodes (that havent been made yet) + /// + /// Second pass: + /// - create an edge between each arena index and the elem that it represents, make an edge between them + /// - if elem is a reference, create nodes for the ContextVar that it depends on, make an edge from the elem to the ContextVar + /// + /// Third pass: + /// - for each ContextVar node, create edges to the arena indices that it depends on + /// + pub fn to_graph( + &self, + graph_backend: &impl GraphBackend, + arena: &mut RangeArena>, + ) -> Graph { + let mut graph = Graph::default(); + let mut arena_idx_to_node_idx = HashMap::new(); + let mut dependency_map: HashMap> = + HashMap::new(); + + // FIRST PASS: create nodes for each arena index + self.inner.iter().for_each(|(arena_idx, _elem)| { + // add an arena node to the graph for the index + let arena_node_idx = graph.add_node(ArenaNode::ARENA(*arena_idx)); + arena_idx_to_node_idx.insert(arena_idx, arena_node_idx); + }); + + // SECOND PASS: create an edge between each arena index and the elem that it represents + self.inner.iter().for_each(|(arena_idx, elem)| { + // get the arena_node_idx for the arena index + let arena_node_idx = arena_idx_to_node_idx.get(arena_idx).unwrap(); + + // add a node for what the arena index has underlying it, and maybe edges for that elem + let underlying_node_idx = match elem { + Elem::Reference(_reference) => { + let node_str = elem.arena_graph_node_label(); + let node_idx = graph.add_node(ArenaNode::ELEM(node_str)); + + // attempt to add in the ContextVar node that the elem is referencing + let context_var_nodes = elem + .dependent_on(graph_backend, arena) + .into_iter() + .collect::>(); + context_var_nodes.into_iter().for_each(|dep_elem| { + let dep_node_idx = + if let Some(&existing_node_idx) = dependency_map.get(&dep_elem) { + // don't make a new ContextVar node, just use the existing one + existing_node_idx + } else { + // make a new ContextVar Node for the Arena graph + let new_node_idx = graph.add_node(ArenaNode::CVAR(format!( + "{}", + dep_elem.as_dot_str(graph_backend, arena) + ))); + dependency_map.insert(dep_elem.clone(), new_node_idx); + new_node_idx + }; + // add an edge from the node to its dependency node + graph.add_edge(node_idx, dep_node_idx, ArenaEdge::VAR); + }); + + node_idx + } + Elem::ConcreteDyn(_range_dyn) => { + let node_str = elem.arena_graph_node_label(); + let node_idx = graph.add_node(ArenaNode::ELEM(node_str)); + node_idx + } + Elem::Concrete(_range_concrete) => { + let node_str = elem.arena_graph_node_label(); + let node_idx = graph.add_node(ArenaNode::ELEM(node_str)); + node_idx + } + Elem::Expr(range_expr) => { + let node_str = elem.arena_graph_node_label(); + let node_idx = graph.add_node(ArenaNode::ELEM(node_str)); + + // Unbox and check the lhs and rhs to see if they are arena indices + let lhs_arena = match *range_expr.lhs.clone() { + Elem::Arena(lhs) => Some(lhs), + Elem::Reference(_lhs) => { + // println!("LHS is a reference: {}", range_expr.lhs); + // attempt to add in the ContextVar node that the elem is referencing + let context_var_nodes = elem + .dependent_on(graph_backend, arena) + .into_iter() + .collect::>(); + context_var_nodes.iter().for_each(|dep_elem| { + let dep_node_idx = if let Some(&existing_node_idx) = + dependency_map.get(&dep_elem) + { + // don't make a new ContextVar node, just use the existing one + existing_node_idx + } else { + // make a new ContextVar Node for the Arena graph + let new_node_idx = graph.add_node(ArenaNode::CVAR(format!( + "{}", + dep_elem.as_dot_str(graph_backend, arena) + ))); + dependency_map.insert(dep_elem.clone(), new_node_idx); + new_node_idx + }; + // use `update_edge` to avoid adding duplicate edges + graph.update_edge(node_idx, dep_node_idx, ArenaEdge::VAR); + }); + None + } + _ => None, + }; + let rhs_arena = match *range_expr.rhs.clone() { + Elem::Arena(rhs) => { + // println!("RHS is an arena index: {}", range_expr.rhs); + Some(rhs) + } + Elem::Reference(_rhs) => { + // println!("RHS is a reference: {}", range_expr.rhs); + // attempt to add in the ContextVar node that the elem is referencing + let context_var_nodes = elem + .dependent_on(graph_backend, arena) + .into_iter() + .collect::>(); + context_var_nodes.iter().for_each(|dep_elem| { + let dep_node_idx = if let Some(&existing_node_idx) = + dependency_map.get(&dep_elem) + { + // don't make a new ContextVar node, just use the existing one + existing_node_idx + } else { + // make a new ContextVar Node for the Arena graph + let new_node_idx = graph.add_node(ArenaNode::CVAR(format!( + "{}", + dep_elem.as_dot_str(graph_backend, arena) + ))); + dependency_map.insert(dep_elem.clone(), new_node_idx); + new_node_idx + }; + // use `update_edge` to avoid adding duplicate edges + graph.update_edge(node_idx, dep_node_idx, ArenaEdge::VAR); + }); + None + } + _ => None, + }; + + // Add edges to the arena indices if they exist + if let Some(lhs_idx) = lhs_arena { + if let Some(&lhs_node_idx) = arena_idx_to_node_idx.get(&lhs_idx) { + graph.add_edge(node_idx, lhs_node_idx, ArenaEdge::LHS); + } + } + + if let Some(rhs_idx) = rhs_arena { + if let Some(&rhs_node_idx) = arena_idx_to_node_idx.get(&rhs_idx) { + graph.add_edge(node_idx, rhs_node_idx, ArenaEdge::RHS); + } + } + node_idx + } + Elem::Arena(range_arena_idx) => { + panic!( + "Arena index in elems: {:?}. This should not happen!", + range_arena_idx + ); + } + Elem::Null => { + let node_str = format!("null"); + let node_idx = graph.add_node(ArenaNode::ELEM(node_str)); + node_idx + } + }; + + // draw edge from the arena_node to the underlying node + graph.add_edge(*arena_node_idx, underlying_node_idx, ArenaEdge::NONE); + }); + + // THIRD PASS - iterate over ContextVarNodes + // iterate over the dependency map and add edges between the ContextVar nodes and the arena nodes + // println!("dependency map: {:?}", dependency_map); + for (cvar_node, &node_idx) in dependency_map.iter() { + // println!("cvar node: {:?}, node idx: {:?}", cvar_node, node_idx); + // Find the appropriate arena_idx for range.min and range.max using Elems.inner + if let Ok(Some(range_min)) = cvar_node.range_min(graph_backend) { + // println!(" range min: {:?}", range_min); + match range_min { + Elem::Arena(arena_idx) => { + // Make a direct edge to the arena node + // println!(" arena idx: {}", arena_idx); + if let Some(&min_node_idx) = arena_idx_to_node_idx.get(&arena_idx) { + graph.add_edge(node_idx, min_node_idx, ArenaEdge::MIN); + } + } + _ => { + // attempt to find the elem in our `inner` and get the associated arena_graph index + let min_arena_idx = self + .inner + .iter() + .find(|(_, elem)| elem == &range_min) + .map(|(idx, _)| *idx); + // Add edges to the min arena indices + if let Some(min_idx) = min_arena_idx { + // println!(" min idx: {:?}", min_idx); + if let Some(&min_node_idx) = arena_idx_to_node_idx.get(&min_idx) { + // println!(" min node idx: {:?}", min_node_idx); + graph.add_edge(node_idx, min_node_idx, ArenaEdge::MIN); + } + } + } + } + } + + if let Ok(Some(range_max)) = cvar_node.range_max(graph_backend) { + // println!(" range max: {:?}", range_max); + match range_max { + Elem::Arena(arena_idx) => { + // Make a direct edge to the arena node + // println!(" arena idx: {}", arena_idx); + if let Some(&max_node_idx) = arena_idx_to_node_idx.get(&arena_idx) { + graph.add_edge(node_idx, max_node_idx, ArenaEdge::MAX); + } + } + _ => { + // attempt to find the elem in our `inner` and get the associated arena_graph index + let max_arena_idx = self + .inner + .iter() + .find(|(_, elem)| elem == &range_max) + .map(|(idx, _)| *idx); + // Add edges to the min arena indices + if let Some(max_idx) = max_arena_idx { + // println!(" max idx: {:?}", max_idx); + if let Some(&max_node_idx) = arena_idx_to_node_idx.get(&max_idx) { + // println!(" max node idx: {:?}", max_node_idx); + graph.add_edge(node_idx, max_node_idx, ArenaEdge::MAX); + } + } + } + } + } + } + + // Ensure the graph does not have a cycle + debug_assert!( + !petgraph::algo::is_cyclic_directed(&graph), + "The graph contains a cycle!" + ); + + graph + } +} + +pub fn mermaid_str(graph: &Graph) -> String { + let mut mermaid_str = Vec::new(); + // let raw_start_str = "flowchart TB\n"; + let raw_start_str = r#" +%%{ +init : { +'theme': 'base', +'themeVariables': { + 'primaryColor': '#1a1b26', + 'primaryTextColor': '#d5daf0', + 'primaryBorderColor': '#4c4c4c', + 'lineColor': '#414868', + 'secondaryColor': '#24283b', + 'tertiaryColor': '#24283b' +}, +"flowchart" : { + "defaultRenderer": "elk" +} +} +}%% + +flowchart TB +"#; + mermaid_str.push(raw_start_str.to_string()); + + let nodes_str = graph + .node_indices() + .map(|idx| { + let node_str = arena_mermaid_node(graph, "\t", idx, true, true, None); + node_str + }) + .collect::>() + .join("\n"); + + let edges_str = graph + .edge_indices() + .enumerate() + .map(|(_i, edge)| { + let (from, to) = graph.edge_endpoints(edge).unwrap(); + let edge_label = format!("{}", graph[edge]); + if edge_label == "" { + // don't do a label + format!(" {} --> {}", from.index(), to.index(),) + } else { + // do an edge label + format!( + " {} -->|\"{}\"| {}", + from.index(), + edge_label, + to.index(), + ) + } + }) + .collect::>() + .join("\n"); + + mermaid_str.push(nodes_str); + mermaid_str.push(edges_str); + + // Make an invisible node that holds all our edge information for coloring later on frontend + let data_str = graph + .edge_indices() + .enumerate() + .map(|(_i, edge)| { + let (from, to) = graph.edge_endpoints(edge).unwrap(); + format!("LS-{}_LE-{}_{}", from.index(), to.index(), &graph[edge]) + }) + .collect::>() + .join(";"); + + let invis_data = format!( + " {}(\"{}\"):::INVIS\n classDef INVIS display:none", + graph.node_count(), + data_str + ); + mermaid_str.push(invis_data); + mermaid_str.join("\n") +} + +pub fn arena_mermaid_node( + graph: &Graph, + indent: &str, + idx: NodeIdx, + style: bool, + _loc: bool, + class: Option<&str>, +) -> String { + let node = &graph[idx]; + let mut node_str = match node { + ArenaNode::ARENA(arena_idx) => { + format!("{indent}{}{{{{\"{}\"}}}}", idx.index(), arena_idx) + } + ArenaNode::ELEM(label) => { + format!("{indent}{}(\"{}\")", idx.index(), label.replace("\"", "'")) + } + ArenaNode::CVAR(label) => { + format!("{indent}{}(\"{}\")", idx.index(), label.replace("\"", "'")) + } + }; + + if style { + node_str.push_str(&format!( + "\n{indent}style {} fill:{}", + idx.index(), + node.dot_str_color() + )); + } + + if let Some(class) = class { + node_str.push_str(&format!("\n{indent}class {} {class}", idx.index(),)); + } + + node_str +} + +fn _calculate_hash(t: &T) -> u64 { + let mut s = DefaultHasher::new(); + t.hash(&mut s); + s.finish() +} impl GraphBackend for Analyzer {} @@ -128,6 +682,10 @@ impl GraphDot for Analyzer { return None; } + if handled_nodes.lock().unwrap().contains(child) { + return None; + } + let post_str = match self.node(*child) { Node::Context(c) => { *cluster_num += 2; @@ -363,7 +921,7 @@ impl GraphDot for Analyzer { let raw_start_str = r##"digraph G { node [shape=box, style="filled, rounded", color="#565f89", fontcolor="#d5daf0", fontname="Helvetica", fillcolor="#24283b"]; edge [color="#414868", fontcolor="#c0caf5", fontname="Helvetica"]; - bgcolor="#1a1b26"; rankdir="BT"; splines=ortho;"##; + bgcolor="#1a1b26"; rankdir="BT"; splines=ortho; size="6,6"; ratio="fill";layout="fdp";"##; dot_str.push(raw_start_str.to_string()); let handled_edges = Arc::new(Mutex::new(BTreeSet::new())); let handled_nodes = Arc::new(Mutex::new(BTreeSet::new())); @@ -461,9 +1019,9 @@ impl GraphDot for Analyzer { ); let mut dot_str = Vec::new(); let raw_start_str = r##"digraph G { - node [shape=box, style="filled, rounded", color="#565f89", fontcolor="#d5daf0", fontname="Helvetica", fillcolor="#24283b"]; - edge [color="#414868", fontcolor="#c0caf5", fontname="Helvetica"]; - bgcolor="#1a1b26";"##; + node [shape=box, style="filled, rounded", color="#565f89", fontcolor="#d5daf0", fontname="Helvetica", fillcolor="#24283b"]; + edge [color="#414868", fontcolor="#c0caf5", fontname="Helvetica"]; + bgcolor="#1a1b26"; rankdir="BT"; splines=ortho; size="6,6"; ratio="fill";layout="fdp";"##; dot_str.push(raw_start_str.to_string()); let nodes_and_edges_str = format!( "{:?}", @@ -638,11 +1196,50 @@ impl GraphLike for G<'_> { fn graph(&self) -> &Graph { self.graph } - fn range_arena(&self) -> &RangeArena> { - panic!("Should not call this") +} + +impl GraphDot for G<'_> { + type T = Elem; + + fn cluster_str( + &self, + _arena: &mut RangeArena, + _node: NodeIdx, + _cluster_num: &mut usize, + _is_killed: bool, + _handled_nodes: Arc>>, + _handled_edges: Arc>>>, + _depth: usize, + _as_mermaid: bool, + ) -> Option + where + Self: std::marker::Sized, + { + todo!() } - fn range_arena_mut(&mut self) -> &mut RangeArena> { - panic!("Should not call this") + + fn dot_str(&self, _arena: &mut RangeArena) -> String + where + Self: std::marker::Sized, + Self: shared::AnalyzerLike, + { + todo!() + } + + fn dot_str_no_tmps(&self, _arena: &mut RangeArena) -> String + where + Self: std::marker::Sized, + Self: GraphLike + shared::AnalyzerLike, + { + todo!() + } + + fn mermaid_str(&self, _arena: &mut RangeArena) -> String + where + Self: std::marker::Sized, + Self: shared::AnalyzerLike, + { + todo!() } } @@ -672,16 +1269,35 @@ pub fn mermaid_node( } if loc { - match g.node(node) { + let current_node = node; + match g.node(current_node) { Node::ContextVar(..) => { - if let solang_parser::pt::Loc::File(f, s, e) = - graph::nodes::ContextVarNode::from(node).loc(g).unwrap() - { + // highlight self + if let Ok(loc) = graph::nodes::ContextVarNode::from(current_node).loc(g) { + if let solang_parser::pt::Loc::File(f, s, e) = loc { + node_str.push_str(&format!( + "\n{indent}class {} loc_{f}_{s}_{e}", + petgraph::graph::GraphIndex::index(¤t_node) + )); + } + } + + // color the forks + let ctx_node = graph::nodes::ContextVarNode::from(current_node).ctx(g); + gather_context_info(g, indent, ctx_node, current_node, &mut node_str); + } + Node::Context(ctx) => { + // highlight self + if let solang_parser::pt::Loc::File(f, s, e) = ctx.loc { node_str.push_str(&format!( "\n{indent}class {} loc_{f}_{s}_{e}", - petgraph::graph::GraphIndex::index(&node) + petgraph::graph::GraphIndex::index(¤t_node) )); } + + // color the forks + let ctx_node = graph::nodes::ContextNode::from(current_node); + gather_context_info(g, indent, ctx_node, current_node, &mut node_str); } _ => {} } @@ -696,3 +1312,47 @@ pub fn mermaid_node( node_str } + +fn gather_context_info( + g: &impl GraphBackend, + indent: &str, + mut ctx_node: ContextNode, + original_cvar_node: NodeIdx, + node_str: &mut String, +) { + loop { + let mut found_continue = false; + let current_loc = ctx_node.underlying(g).unwrap().loc; + for edge in g + .graph() + .edges_directed(ctx_node.into(), Direction::Outgoing) + { + if let Edge::Context(ContextEdge::Continue(true_or_false)) = edge.weight() { + let target_node = edge.target(); + if let Node::Context(_ctx) = g.node(target_node) { + // error!("found continue pointing to node"); + ctx_node = target_node.into(); + found_continue = true; + // Gather the edge weight and the loc of the Context node it points to + if let solang_parser::pt::Loc::File(f, s, e) = current_loc { + let fork_str = format!( + "\n{indent}class {} fork_{f}_{s}_{e}_{}", + petgraph::graph::GraphIndex::index(&original_cvar_node), + match *true_or_false { + "fork_true" => true, + "fork_false" => false, + _ => false, + } + ); + // error!("in gather_context_info, {:?}", fork_str); + node_str.push_str(&fork_str); + } + break; + } + } + } + if !found_continue { + break; + } + } +} diff --git a/crates/pyrometer/src/lib.rs b/crates/pyrometer/src/lib.rs index bedfe8d6..be40bd1c 100644 --- a/crates/pyrometer/src/lib.rs +++ b/crates/pyrometer/src/lib.rs @@ -1,6 +1,6 @@ mod analyzer; mod analyzer_backend; mod builtin_fns; -mod graph_backend; +pub mod graph_backend; pub use analyzer::*; diff --git a/crates/shared/Cargo.toml b/crates/shared/Cargo.toml index 22e1c17b..a69f834d 100644 --- a/crates/shared/Cargo.toml +++ b/crates/shared/Cargo.toml @@ -17,4 +17,8 @@ ethers-core.workspace = true hex.workspace = true tracing.workspace = true tracing-subscriber.workspace = true -ahash.workspace = true \ No newline at end of file +ahash.workspace = true + +tokio = { version = "1", features = ["full"] } +serde = { version = "1", features = ["derive"] } +reqwest = { version = "0.12", features = ["json"] } diff --git a/crates/shared/src/analyzer_like.rs b/crates/shared/src/analyzer_like.rs index e4cd7b25..1145e4af 100644 --- a/crates/shared/src/analyzer_like.rs +++ b/crates/shared/src/analyzer_like.rs @@ -183,4 +183,5 @@ pub trait AnalyzerLike: GraphLike { fn join_stats_mut(&mut self) -> &mut JoinStats; fn handled_funcs(&self) -> &[Self::FunctionNode]; fn handled_funcs_mut(&mut self) -> &mut Vec; + fn file_mapping(&self) -> BTreeMap; } diff --git a/crates/shared/src/graph_like.rs b/crates/shared/src/graph_like.rs index 041f99aa..ae073436 100644 --- a/crates/shared/src/graph_like.rs +++ b/crates/shared/src/graph_like.rs @@ -12,6 +12,14 @@ use std::{ sync::{Arc, Mutex}, }; +use reqwest::Client; +use serde::{Deserialize, Serialize}; +use std::time::{SystemTime, UNIX_EPOCH}; +use tokio::runtime::Runtime; +use tracing::{error, trace}; + +pub static mut USE_DEBUG_SITE: bool = false; + pub type NodeIdx = NodeIndex; pub type EdgeIdx = EdgeIndex; pub type RangeArenaIdx = usize; @@ -58,22 +66,6 @@ pub trait GraphLike { self.graph_mut() .add_edge(from_node.into(), to_node.into(), edge.into()); } - - fn range_arena(&self) -> &RangeArena; - fn range_arena_mut(&mut self) -> &mut RangeArena; - fn try_take_range_arena(&mut self) -> Option> { - let arena = self.range_arena_mut(); - if !arena.ranges.is_empty() { - Some(std::mem::take(arena)) - } else { - None - } - } - - fn take_range_arena(&mut self) -> RangeArena { - let arena = self.range_arena_mut(); - std::mem::take(arena) - } } /// A trait that constructs dot-like visualization strings (either mermaid or graphviz) @@ -186,3 +178,46 @@ pub trait GraphDot: GraphLike { Self: std::marker::Sized, Self: AnalyzerLike; } + +#[derive(Serialize, Deserialize, Debug)] +struct GraphMessage { + graph: String, + timestamp: u64, +} + +pub fn post_to_site(graph: &G, arena: &mut RangeArena) +where + G: GraphDot + AnalyzerLike, +{ + let rt = Runtime::new().unwrap(); + rt.block_on(async { + post_to_site_async(graph, arena).await; + }); +} + +async fn post_to_site_async(graph: &G, arena: &mut RangeArena) +where + G: GraphDot + AnalyzerLike, +{ + let client = Client::new(); + let graph_msg = GraphMessage { + graph: graph.mermaid_str(arena), + timestamp: SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("Time went backwards") + .as_secs(), + }; + + let res = client + .post("http://127.0.0.1:8545/addgraph") + .json(&graph_msg) + .send() + .await + .expect("Failed to send request"); + + if res.status().is_success() { + trace!("Successfully posted dot to site"); + } else { + error!("Failed to post graph to site: {:?}", res.status()); + } +}