diff --git a/VEX/priv/guest_arm64_toIR.c b/VEX/priv/guest_arm64_toIR.c index 6e77b34c7..99f17993d 100644 --- a/VEX/priv/guest_arm64_toIR.c +++ b/VEX/priv/guest_arm64_toIR.c @@ -7422,7 +7422,7 @@ Bool dis_ARM64_branch_etc(/*MB_OUT*/DisResult* dres, UInt insn, /* -------------------- B{L} uncond -------------------- */ if (INSN(30,26) == BITS5(0,0,1,0,1)) { /* 000101 imm26 B (PC + sxTo64(imm26 << 2)) - 100101 imm26 B (PC + sxTo64(imm26 << 2)) + 100101 imm26 BL (PC + sxTo64(imm26 << 2)) */ UInt bLink = INSN(31,31); ULong uimm64 = INSN(25,0) << 2; @@ -7432,7 +7432,7 @@ Bool dis_ARM64_branch_etc(/*MB_OUT*/DisResult* dres, UInt insn, } putPC(mkU64(guest_PC_curr_instr + simm64)); dres->whatNext = Dis_StopHere; - dres->jk_StopHere = Ijk_Call; + dres->jk_StopHere = bLink ? Ijk_Call : Ijk_Boring; DIP("b%s 0x%llx\n", bLink == 1 ? "l" : "", guest_PC_curr_instr + simm64); return True; diff --git a/callgrind-utils/.gitignore b/callgrind-utils/.gitignore new file mode 100644 index 000000000..435dc4ab4 --- /dev/null +++ b/callgrind-utils/.gitignore @@ -0,0 +1,2 @@ +target/ +*.svg \ No newline at end of file diff --git a/callgrind-utils/Cargo.lock b/callgrind-utils/Cargo.lock new file mode 100644 index 000000000..d0e444459 --- /dev/null +++ b/callgrind-utils/Cargo.lock @@ -0,0 +1,1562 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom 0.3.4", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[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 = "anyhow" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" + +[[package]] +name = "arrayvec" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f02882884d3e1bc524fb12c79f107f6ad0e1cfd498c536ffb494301740995dfe" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593" + +[[package]] +name = "callgraph-shared" +version = "0.1.0" +source = "git+ssh://git@github.com/CodSpeedHQ/platform.git#c7599c0ba2f9b355c5404530244feef50e0262e6" +dependencies = [ + "anyhow", + "hashbrown 0.15.5", + "itertools", + "petgraph", + "runner-shared", + "rustc-hash", + "schemars 1.2.1", + "serde", + "serde_json", + "serde_with", + "tokio", + "tracing", + "typetag", +] + +[[package]] +name = "callgrind-parser" +version = "0.1.0" +source = "git+ssh://git@github.com/CodSpeedHQ/platform.git#c7599c0ba2f9b355c5404530244feef50e0262e6" +dependencies = [ + "anyhow", + "callgraph-shared", + "derive_builder", + "itertools", + "lazy_static", + "petgraph", + "serde", + "tokio-stream", + "tracing", + "typetag", +] + +[[package]] +name = "callgrind-utils" +version = "0.1.0" +dependencies = [ + "callgraph-shared", + "callgrind-parser", + "inferno", + "insta", + "petgraph", + "rstest", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "cc" +version = "1.2.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "num-traits", + "serde", + "windows-link", +] + +[[package]] +name = "console" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d64e8af5551369d19cf50138de61f1c42074ab970f74e99be916646777f8fc87" +dependencies = [ + "encode_unicode", + "libc", + "windows-sys", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core 0.20.11", + "darling_macro 0.20.11", +] + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core 0.23.0", + "darling_macro 0.23.0", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core 0.20.11", + "quote", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core 0.23.0", + "quote", + "syn", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "serde_core", +] + +[[package]] +name = "derive_builder" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" +dependencies = [ + "darling 0.20.11", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" +dependencies = [ + "derive_builder_core", + "syn", +] + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "erased-serde" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec" +dependencies = [ + "serde", + "serde_core", + "typeid", +] + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-timer" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af43fadb8a98512d547e37b4e92e0ced13e205c061b87b4623eff01d918d6968" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi 5.3.0", + "wasip2", +] + +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "inferno" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90807d610575744524d9bdc69f3885d96f0e6c3354565b0828354a7ff2a262b8" +dependencies = [ + "ahash", + "itoa", + "log", + "num-format", + "once_cell", + "quick-xml", + "rgb", + "str_stack", +] + +[[package]] +name = "insta" +version = "1.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f0f8fee8c926415c58d6ae43a08523a26faccb2323f5e6b644fe7dd4ef6b82" +dependencies = [ + "console", + "once_cell", + "similar", + "tempfile", +] + +[[package]] +name = "inventory" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4f0c30c76f2f4ccee3fe55a2435f691ca00c0e4bd87abe4f4a851b1d4dac39b" +dependencies = [ + "rustversion", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "linux-perf-event-reader" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa8fc7e83909ea3b9e2784591655637d3401f2f16014f9d8d6e23ccd138e665f" +dependencies = [ + "bitflags", + "byteorder", + "memchr", + "thiserror", +] + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "log" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" + +[[package]] +name = "memchr" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-format" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" +dependencies = [ + "arrayvec", + "itoa", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "petgraph" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" +dependencies = [ + "fixedbitset", + "indexmap 2.14.0", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quick-xml" +version = "0.39.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "regex" +version = "1.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + +[[package]] +name = "relative-path" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" + +[[package]] +name = "rgb" +version = "0.8.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "rmp" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ba8be72d372b2c9b35542551678538b562e7cf86c3315773cae48dfbfe7790c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "rmp-serde" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f81bee8c8ef9b577d1681a70ebbc962c232461e397b22c208c43c04b67a155" +dependencies = [ + "rmp", + "serde", +] + +[[package]] +name = "rstest" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a2c585be59b6b5dd66a9d2084aa1d8bd52fbdb806eafdeffb52791147862035" +dependencies = [ + "futures", + "futures-timer", + "rstest_macros", + "rustc_version", +] + +[[package]] +name = "rstest_macros" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "825ea780781b15345a146be27eaefb05085e337e869bff01b4306a4fd4a9ad5a" +dependencies = [ + "cfg-if", + "glob", + "proc-macro-crate", + "proc-macro2", + "quote", + "regex", + "relative-path", + "rustc_version", + "syn", + "unicode-ident", +] + +[[package]] +name = "runner-shared" +version = "0.1.0" +source = "git+https://github.com/CodSpeedHQ/codspeed?rev=9e21a9c0415c848d1c6d7e66c221f7524433899d#9e21a9c0415c848d1c6d7e66c221f7524433899d" +dependencies = [ + "anyhow", + "bincode", + "itertools", + "libc", + "linux-perf-event-reader", + "log", + "rmp", + "rmp-serde", + "serde", + "serde_json", + "zstd", +] + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +dependencies = [ + "dyn-clone", + "ref-cast", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d115b50f4aaeea07e79c1912f645c7513d81715d0420f8bc77a18c6260b307f" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_with" +version = "3.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c" +dependencies = [ + "base64", + "bs58", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.14.0", + "schemars 0.9.0", + "schemars 1.2.1", + "serde_core", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" +dependencies = [ + "darling 0.23.0", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "similar" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "str_stack" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f446288b699d66d0fd2e30d1cfe7869194312524b3b9252594868ed26ef056a" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.3", + "once_cell", + "rustix", + "windows-sys", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18dfaaeddcb932337b5e7866ee7d0ce9b76d2fd092997146f187ec09b4558a50" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" + +[[package]] +name = "time-macros" +version = "0.2.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c431b87111666e491a90baa837f914fb45cd5dc3c268591b0220ff5057f2085f" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +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.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +dependencies = [ + "bytes", + "pin-project-lite", + "tokio-macros", +] + +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-stream" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.25.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" +dependencies = [ + "indexmap 2.14.0", + "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + +[[package]] +name = "typetag" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5a897b12c6c1151ad0b138b8db50252dc301f93bc3b027db05eec82aeed298c" +dependencies = [ + "erased-serde", + "inventory", + "once_cell", + "serde", + "typetag-impl", +] + +[[package]] +name = "typetag-impl" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf808357c6ed7e13ba0f3277ec8d8f21b2d501274895104263985330c726c1c5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasip2" +version = "1.0.4+wasi-0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "winnow" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "zerocopy" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[package]] +name = "zstd" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.16+zstd.1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/callgrind-utils/Cargo.toml b/callgrind-utils/Cargo.toml new file mode 100644 index 000000000..01b672ac2 --- /dev/null +++ b/callgrind-utils/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "callgrind-utils" +version = "0.1.0" +edition = "2024" + +[dependencies] +inferno = { version = "0.12.6", default-features = false } +callgrind-parser = { git = "ssh://git@github.com/CodSpeedHQ/platform.git", package = "callgrind-parser" } +callgraph-shared = { git = "ssh://git@github.com/CodSpeedHQ/platform.git", package = "callgraph-shared" } +petgraph = "0.7.1" +serde = { version = "1", features = ["derive"] } +serde_json = "1" +thiserror = "2" + +[dev-dependencies] +insta = "1" +rstest = "0.23" diff --git a/callgrind-utils/build.rs b/callgrind-utils/build.rs new file mode 100644 index 000000000..5f5a67dde --- /dev/null +++ b/callgrind-utils/build.rs @@ -0,0 +1,116 @@ +//! Ensures the in-repo Callgrind (`../vg-in-place`) is built before the tests +//! that shell out to it run. +//! +//! The build is incremental: `make` is timestamp-driven, so this is a few +//! seconds when the tree is already current and only does real work when the +//! Callgrind sources change. Build order matters: VEX -> coregrind -> callgrind. + +use std::env; +use std::path::{Path, PathBuf}; +use std::process::Command; + +fn main() { + let repo = PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR")) + .parent() + .expect("crate has a parent directory") + .to_path_buf(); + + track_sources(&repo); + + // Callgrind names its tool binary after the target: callgrind--linux. + let arch = match env::consts::ARCH { + "x86_64" => "amd64", + "aarch64" => "arm64", + other => panic!("unsupported arch for the Callgrind build: {other}"), + }; + + configure_if_needed(&repo); + build(&repo); + assert_artifacts(&repo, arch); +} + +/// Rebuild when a hand-written Callgrind source changes. Only the top-level +/// `callgrind/*.c` / `*.h` are tracked: the `tests/` subdir accumulates +/// `callgrind.out.*` / `vgcore.*` on every run, which would otherwise +/// re-trigger this build on each test invocation. +fn track_sources(repo: &Path) { + println!("cargo:rerun-if-changed=build.rs"); + println!( + "cargo:rerun-if-changed={}", + repo.join("configure").display() + ); + + let cg = repo.join("callgrind"); + let entries = std::fs::read_dir(&cg).unwrap_or_else(|e| panic!("read {}: {e}", cg.display())); + for path in entries.flatten().map(|e| e.path()) { + if matches!(path.extension().and_then(|e| e.to_str()), Some("c" | "h")) { + println!("cargo:rerun-if-changed={}", path.display()); + } + } +} + +/// `configure` is checked in, so this only runs on a pristine tree. Callgrind +/// cycle estimation needs Capstone; `nix develop` exports `CAPSTONE_DIR`, which +/// `configure` picks up. Fail loudly if it is missing rather than emitting a +/// cryptic configure error. +fn configure_if_needed(repo: &Path) { + if repo.join("Makefile").is_file() { + return; + } + + assert!( + env::var_os("CAPSTONE_DIR").is_some(), + "valgrind-codspeed is not configured and CAPSTONE_DIR is unset.\n\ + Build from inside `nix develop` (which exports CAPSTONE_DIR), or configure\n\ + manually: ./configure --enable-only64bit --with-capstone=PATH" + ); + + run(Command::new("./configure") + .arg("--enable-only64bit") + .current_dir(repo)); +} + +fn build(repo: &Path) { + let jobs = format!( + "-j{}", + std::thread::available_parallelism() + .map(|n| n.get()) + .unwrap_or(1) + ); + + run(Command::new("make") + .arg("include/vgversion.h") + .current_dir(repo)); + for dir in ["VEX", "coregrind", "callgrind"] { + run(Command::new("make") + .arg(&jobs) + .arg("-C") + .arg(dir) + .current_dir(repo)); + } +} + +/// The three artifacts `vg-in-place` execs: the launcher, the tool, and the +/// `.in_place` symlink the launcher resolves via `VALGRIND_LIB`. +fn assert_artifacts(repo: &Path, arch: &str) { + let tool = format!("callgrind-{arch}-linux"); + for path in [ + repo.join("coregrind/valgrind"), + repo.join("callgrind").join(&tool), + repo.join(".in_place").join(&tool), + ] { + assert!( + path.exists(), + "expected build artifact missing after make: {}", + path.display() + ); + } +} + +fn run(cmd: &mut Command) { + let shown = format!("{cmd:?}"); + let status = cmd + .status() + .unwrap_or_else(|e| panic!("failed to spawn {shown}: {e}")); + assert!(status.success(), "command failed ({status}): {shown}"); +} diff --git a/callgrind-utils/src/error.rs b/callgrind-utils/src/error.rs new file mode 100644 index 000000000..7459e116f --- /dev/null +++ b/callgrind-utils/src/error.rs @@ -0,0 +1,36 @@ +use thiserror::Error; + +/// Errors raised while parsing a Callgrind `.out` file. +#[derive(Debug, Error)] +pub enum ParseError { + #[error("I/O error: {0}")] + Io(#[from] std::io::Error), + #[error("bad id: {0}")] + BadId(#[from] std::num::ParseIntError), + #[error("call record missing required cfn=")] + MissingCfn, + #[error("unexpected end of input")] + UnexpectedEof, + #[error("callgrind-parser error: {0}")] + External(String), +} + +/// Errors raised while serializing a `CallGraph` to JSON. +#[derive(Debug, Error)] +pub enum ToJsonError { + #[error("serde error: {0}")] + Serde(#[from] serde_json::Error), + #[error("I/O error: {0}")] + Io(#[from] std::io::Error), +} + +/// Errors raised while rendering a `CallGraph` to a flamegraph SVG. +#[derive(Debug, Error)] +pub enum FlamegraphError { + #[error("the graph carries no cost data (all self/inclusive costs are zero)")] + NoCost, + #[error("inferno flamegraph error: {0}")] + Inferno(String), + #[error("I/O error: {0}")] + Io(#[from] std::io::Error), +} diff --git a/callgrind-utils/src/flamegraph.rs b/callgrind-utils/src/flamegraph.rs new file mode 100644 index 000000000..eb923205e --- /dev/null +++ b/callgrind-utils/src/flamegraph.rs @@ -0,0 +1,202 @@ +use std::collections::HashMap; + +use callgraph_shared::interfaces::CostVec; +use inferno::flamegraph::{self, Options}; +use petgraph::{ + graph::NodeIndex, + visit::{EdgeRef, IntoEdgeReferences}, +}; + +use super::{error::FlamegraphError, model::CallGraph}; + +const MIN_BUDGET: f64 = 1.0; +const MIN_BUDGET_FRACTION: f64 = 0.0005; + +impl CallGraph { + pub fn to_folded_without_costs(&self) -> Vec { + self.to_folded() + .iter() + .map(|line| { + let mut parts = line.split_whitespace(); + let stack = parts.next().unwrap_or_default(); + format!("{stack} ") + }) + .collect() + } + + pub fn to_folded(&self) -> Vec { + let graph = self.inner.graph.borrow(); + let indices: Vec = graph.node_indices().collect(); + let index_by_node: HashMap = indices + .iter() + .enumerate() + .map(|(index, node)| (*node, index)) + .collect(); + + let names: Vec<&str> = indices + .iter() + .map(|index| graph[*index].name.as_str()) + .collect(); + let self_costs: Vec = indices + .iter() + .map(|index| first_cost(&graph[*index].costs.aggregated_cost())) + .collect(); + + let n = indices.len(); + let mut out = vec![Vec::<(usize, u64)>::new(); n]; + let mut incoming_incl = vec![0; n]; + for edge in graph.edge_references() { + let Some(&caller) = index_by_node.get(&edge.source()) else { + continue; + }; + let Some(&callee) = index_by_node.get(&edge.target()) else { + continue; + }; + let inclusive_cost = first_cost(&edge.weight().costs.aggregated_cost()); + out[caller].push((callee, inclusive_cost)); + incoming_incl[callee] += inclusive_cost; + } + + let incl: Vec = (0..n) + .map(|i| self_costs[i] + out[i].iter().map(|(_, cost)| *cost).sum::()) + .collect(); + + let roots = roots(&incoming_incl, &incl); + let total: f64 = roots.iter().map(|(_, budget)| *budget).sum(); + let min_budget = MIN_BUDGET.max(total * MIN_BUDGET_FRACTION); + + let mut lines = Vec::new(); + let mut stack = Vec::new(); + let mut on_path = vec![false; n]; + for (root, budget) in roots { + fold_dfs( + root, + budget, + min_budget, + &mut stack, + &mut on_path, + &out, + &incl, + &self_costs, + &names, + &mut lines, + ); + } + lines + } + + pub fn to_flamegraph(&self) -> Result { + let lines = self.to_folded(); + if lines.is_empty() { + return Err(FlamegraphError::NoCost); + } + + let mut opts = Options::default(); + opts.title = "Callgrind".to_string(); + opts.count_name = "instructions".to_string(); + + let mut svg = Vec::new(); + flamegraph::from_lines(&mut opts, lines.iter().map(String::as_str), &mut svg) + .map_err(|e| FlamegraphError::Inferno(e.to_string()))?; + String::from_utf8(svg).map_err(|e| FlamegraphError::Inferno(e.to_string())) + } + + pub fn to_flamegraph_file( + &self, + path: impl AsRef, + ) -> Result<(), FlamegraphError> { + let svg = self.to_flamegraph()?; + std::fs::write(path, svg)?; + Ok(()) + } +} + +fn first_cost(cost: &CostVec) -> u64 { + cost.0.first().copied().unwrap_or(0) +} + +fn roots(incoming_incl: &[u64], incl: &[u64]) -> Vec<(usize, f64)> { + let roots: Vec<(usize, f64)> = (0..incl.len()) + .filter_map(|i| { + let uncovered = incl[i].saturating_sub(incoming_incl[i]); + (uncovered > 0).then_some((i, uncovered as f64)) + }) + .collect(); + if !roots.is_empty() { + return roots; + } + (0..incl.len()) + .filter(|&i| incl[i] > 0) + .max_by_key(|&i| incl[i]) + .map(|i| (i, incl[i] as f64)) + .into_iter() + .collect() +} + +#[allow(clippy::too_many_arguments)] +fn fold_dfs( + node: usize, + budget: f64, + min_budget: f64, + stack: &mut Vec, + on_path: &mut [bool], + out: &[Vec<(usize, u64)>], + incl: &[u64], + self_costs: &[u64], + names: &[&str], + lines: &mut Vec, +) { + if budget < min_budget || incl[node] == 0 { + return; + } + + stack.push(node); + on_path[node] = true; + + let frac = (budget / incl[node] as f64).min(1.0); + let self_here = (self_costs[node] as f64 * frac).round() as u64; + if self_here >= 1 { + lines.push(fold_line(stack, names, self_here)); + } + + for &(child, edge_incl) in &out[node] { + let child_budget = edge_incl as f64 * frac; + if !on_path[child] { + fold_dfs( + child, + child_budget, + min_budget, + stack, + on_path, + out, + incl, + self_costs, + names, + lines, + ); + continue; + } + let recursive = child_budget.round() as u64; + if recursive >= 1 { + stack.push(child); + lines.push(fold_line(stack, names, recursive)); + stack.pop(); + } + } + + on_path[node] = false; + stack.pop(); +} + +fn fold_line(stack: &[usize], names: &[&str], count: u64) -> String { + let mut line = String::new(); + for (i, &idx) in stack.iter().enumerate() { + if i > 0 { + line.push(';'); + } + line.push_str(names[idx]); + } + line.push(' '); + line.push_str(&count.to_string()); + line +} diff --git a/callgrind-utils/src/lib.rs b/callgrind-utils/src/lib.rs new file mode 100644 index 000000000..719c21a32 --- /dev/null +++ b/callgrind-utils/src/lib.rs @@ -0,0 +1,6 @@ +pub mod error; +pub mod flamegraph; +pub mod model; +pub mod parser; +mod redact; +pub mod serialize; diff --git a/callgrind-utils/src/model.rs b/callgrind-utils/src/model.rs new file mode 100644 index 000000000..b18e18646 --- /dev/null +++ b/callgrind-utils/src/model.rs @@ -0,0 +1,11 @@ +use callgrind_parser::graph::CallgrindCallGraph; + +pub struct CallGraph { + pub(crate) inner: CallgrindCallGraph, +} + +impl CallGraph { + pub(crate) fn from_external(inner: CallgrindCallGraph) -> Self { + Self { inner } + } +} diff --git a/callgrind-utils/src/parser.rs b/callgrind-utils/src/parser.rs new file mode 100644 index 000000000..b1f3d3ab0 --- /dev/null +++ b/callgrind-utils/src/parser.rs @@ -0,0 +1,41 @@ +use callgrind_parser::{TraceDataParseErrorKind, trace_data::interfaces::TraceData}; + +use super::{error::ParseError, model::CallGraph}; + +impl CallGraph { + /// Parse a Callgrind `.out` stream into a call graph using the monorepo + /// `callgrind-parser` crate. + pub fn parse(reader: impl std::io::BufRead) -> Result { + let lines = reader.lines().collect::, _>>()?; + let trace_data = parse_trace_data(lines)?; + let part_number = graph_part_number(&trace_data)?; + let graph = callgrind_parser::build_call_graph(&trace_data, part_number); + Ok(CallGraph::from_external(graph)) + } +} + +fn parse_trace_data(lines: Vec) -> Result { + let iter = lines.into_iter().map(Ok); + match callgrind_parser::parse_lines(iter) { + Ok(data) => Ok(data), + Err(err) => { + if matches!(err.kind, TraceDataParseErrorKind::InconsistentData) { + return err + .into_partial_data() + .map_err(|partial_err| ParseError::External(partial_err.to_string())); + } + Err(ParseError::External(err.to_string())) + } + } +} + +fn graph_part_number(trace_data: &TraceData) -> Result { + trace_data + .parts + .iter() + .find(|part| !part.functions.is_empty()) + .map(|part| part.number) + .ok_or_else(|| { + ParseError::External("Callgrind data did not contain any function graph".to_string()) + }) +} diff --git a/callgrind-utils/src/redact.rs b/callgrind-utils/src/redact.rs new file mode 100644 index 000000000..d3e86eb41 --- /dev/null +++ b/callgrind-utils/src/redact.rs @@ -0,0 +1,166 @@ +use callgraph_shared::graph::{PidTid, ThreadCosts, ThreadCounts}; + +use super::model::CallGraph; + +const UNKNOWN: &str = "???"; + +impl CallGraph { + pub fn redact(mut self) -> CallGraph { + self.inner.pid = 0; + { + let mut graph = self.inner.graph.borrow_mut(); + let node_indices: Vec<_> = graph.node_indices().collect(); + for idx in node_indices { + let raw = &mut graph[idx]; + let (function, file, object) = redact_identity( + raw.name.as_str(), + raw.file.as_deref(), + raw.object.as_deref(), + ); + raw.name = function; + raw.file = Some(file); + raw.object = Some(object); + normalize_costs(&mut raw.costs); + } + + let edge_indices: Vec<_> = graph.edge_indices().collect(); + for idx in edge_indices { + let edge = graph.edge_weight_mut(idx).expect("edge index is live"); + normalize_costs(&mut edge.costs); + normalize_counts(&mut edge.counts); + } + } + self + } +} + +fn redact_identity( + function: &str, + file: Option<&str>, + object: Option<&str>, +) -> (String, String, String) { + let object = redact_object(basename(object).unwrap_or(UNKNOWN)); + if is_runtime_object(&object) { + return (UNKNOWN.to_string(), UNKNOWN.to_string(), object); + } + ( + redact_function(function), + basename(file).unwrap_or(UNKNOWN).to_string(), + object, + ) +} + +fn normalize_costs(costs: &mut ThreadCosts) { + let total = costs.aggregated_cost(); + costs.0.clear(); + costs.0.insert(PidTid::UNKNOWN, total); +} + +fn normalize_counts(counts: &mut ThreadCounts) { + let total = counts.aggregated_count(); + counts.0.clear(); + counts.0.insert(PidTid::UNKNOWN, total); +} + +fn basename(value: Option<&str>) -> Option<&str> { + let value = value?; + value.rsplit('/').next().filter(|part| !part.is_empty()) +} + +fn redact_function(function: &str) -> String { + let function = strip_symbol_version(function); + if is_hex_address(function) { + return "".to_string(); + } + function.to_string() +} + +fn strip_symbol_version(function: &str) -> &str { + for marker in ["@@", "@"] { + let Some(index) = function.find(marker) else { + continue; + }; + let version = &function[index + marker.len()..]; + if is_symbol_version(version) { + return &function[..index]; + } + } + function +} + +fn is_symbol_version(version: &str) -> bool { + let Some(first) = version.chars().next() else { + return false; + }; + (first.is_ascii_alphanumeric() || first == '_') + && version + .chars() + .all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '.') +} + +fn is_hex_address(function: &str) -> bool { + let Some(hex) = function.strip_prefix("0x") else { + return false; + }; + !hex.is_empty() && hex.chars().all(|c| c.is_ascii_hexdigit()) +} + +fn redact_object(object: &str) -> String { + if is_loader_soname(object) { + return "ld-linux".to_string(); + } + if let Some(module) = cpython_extension_module(object) { + return format!("{module}.cpython.so"); + } + if is_libffi_soname(object) { + return "libffi.so".to_string(); + } + object.to_string() +} + +fn is_runtime_object(object: &str) -> bool { + object == "ld-linux" || is_libc_soname(object) +} + +fn is_libc_soname(object: &str) -> bool { + let Some(version) = object.strip_prefix("libc.so.") else { + return false; + }; + !version.is_empty() && version.chars().all(|c| c.is_ascii_digit()) +} + +fn cpython_extension_module(object: &str) -> Option<&str> { + let (module, suffix) = object.split_once(".cpython-")?; + let abi = suffix.strip_suffix(".so")?; + if module.is_empty() || abi.is_empty() { + return None; + } + Some(module) +} + +fn is_libffi_soname(object: &str) -> bool { + let Some(version) = object.strip_prefix("libffi.so.") else { + return false; + }; + !version.is_empty() + && version.chars().all(|c| c.is_ascii_digit() || c == '.') + && version.chars().any(|c| c.is_ascii_digit()) +} + +fn is_loader_soname(object: &str) -> bool { + let Some(rest) = object.strip_prefix("ld-") else { + return false; + }; + let Some(index) = rest.find(".so.") else { + return false; + }; + + let loader_name = &rest[..index]; + let soname_version = &rest[index + ".so.".len()..]; + !loader_name.is_empty() + && loader_name + .chars() + .all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_') + && !soname_version.is_empty() + && soname_version.chars().all(|c| c.is_ascii_digit()) +} diff --git a/callgrind-utils/src/serialize.rs b/callgrind-utils/src/serialize.rs new file mode 100644 index 000000000..0ff2c4eae --- /dev/null +++ b/callgrind-utils/src/serialize.rs @@ -0,0 +1,53 @@ +use serde_json::{Map, Value}; + +use super::{error::ToJsonError, model::CallGraph}; + +impl CallGraph { + pub fn to_json(&self) -> Result { + let mut value = serde_json::to_value(&self.inner)?; + redact_json_metadata(&mut value); + serde_json::to_string_pretty(&value) + } + + pub fn to_json_file(&self, path: impl AsRef) -> Result<(), ToJsonError> { + let s = self.to_json()?; + std::fs::write(path, s)?; + Ok(()) + } +} + +fn redact_json_metadata(value: &mut Value) { + match value { + Value::Object(object) => redact_object(object), + Value::Array(items) => { + for item in items { + redact_json_metadata(item); + } + } + _ => {} + } +} + +fn redact_object(object: &mut Map) { + if object.contains_key("timeDistribution") { + object.insert("timeDistribution".to_string(), Value::Array(Vec::new())); + } + if object.contains_key("pid") { + object.insert("pid".to_string(), Value::from(0)); + } + if object.contains_key("tid") { + object.insert("tid".to_string(), Value::from(0)); + } + if object.contains_key("processes") { + object.insert( + "processes".to_string(), + Value::Object(Map::from_iter([( + "0".to_string(), + Value::Object(Map::new()), + )])), + ); + } + for child in object.values_mut() { + redact_json_metadata(child); + } +} diff --git a/callgrind-utils/testdata/chain.c b/callgrind-utils/testdata/chain.c new file mode 100644 index 000000000..cb9360263 --- /dev/null +++ b/callgrind-utils/testdata/chain.c @@ -0,0 +1,27 @@ +// Fixture: a linear call chain `main -> a -> b -> c` (no recursion, no shared +// callees). See recursion.c for the instrumentation/build conventions. + +#include + +static int c(int n) { + return n + 1; +} + +static int b(int n) { + return c(n) + 1; +} + +static int a(int n) { + return b(n) + 1; +} + +int main(void) { + CALLGRIND_START_INSTRUMENTATION; + CALLGRIND_ZERO_STATS; + + volatile int sink = a(5); + (void)sink; + + CALLGRIND_STOP_INSTRUMENTATION; + return 0; +} diff --git a/callgrind-utils/testdata/clgctl.c b/callgrind-utils/testdata/clgctl.c new file mode 100644 index 000000000..3deb59341 --- /dev/null +++ b/callgrind-utils/testdata/clgctl.c @@ -0,0 +1,28 @@ +// Callgrind client-request shim for the Python fixture (`recursion.py`). +// +// The CALLGRIND_* client requests are inline-asm sequences, so they can't be +// issued from pure Python. The Python fixture loads this shared library via +// `ctypes` and calls these entry points to drive instrumentation, mirroring +// what pytest-codspeed's instrument-hooks does: skip the Python runtime objects +// at runtime, then START/ZERO around the measured region and STOP after. +// +// Build (shared, against the in-repo client-request headers): +// cc -g -O0 -shared -fPIC -I callgrind -I include ... + +#include + +// Add an object file to Callgrind's obj-skip list at runtime. Matching is exact +// against the mapped object path, so the caller passes a realpath (same as +// instrument-hooks' `callgrind_add_obj_skip`). +void clg_add_obj_skip(const char *path) { + CALLGRIND_ADD_OBJ_SKIP(path); +} + +void clg_start(void) { + CALLGRIND_START_INSTRUMENTATION; + CALLGRIND_ZERO_STATS; +} + +void clg_stop(void) { + CALLGRIND_STOP_INSTRUMENTATION; +} diff --git a/callgrind-utils/testdata/diamond.c b/callgrind-utils/testdata/diamond.c new file mode 100644 index 000000000..d617b2759 --- /dev/null +++ b/callgrind-utils/testdata/diamond.c @@ -0,0 +1,32 @@ +// Fixture: a diamond graph where `bottom` is a shared callee reached via two +// paths: `main -> top -> {left, right} -> bottom`. Exercises a node with two +// distinct incoming edges. See recursion.c for the conventions. + +#include + +static int bottom(int n) { + return n * 2; +} + +static int left(int n) { + return bottom(n) + 1; +} + +static int right(int n) { + return bottom(n) + 2; +} + +static int top(int n) { + return left(n) + right(n); +} + +int main(void) { + CALLGRIND_START_INSTRUMENTATION; + CALLGRIND_ZERO_STATS; + + volatile int sink = top(5); + (void)sink; + + CALLGRIND_STOP_INSTRUMENTATION; + return 0; +} diff --git a/callgrind-utils/testdata/fractal.c b/callgrind-utils/testdata/fractal.c new file mode 100644 index 000000000..bde5417cf --- /dev/null +++ b/callgrind-utils/testdata/fractal.c @@ -0,0 +1,247 @@ +// Build with `-g -O0` so the functions are real (no inlining) and carry debug +// names: +// cc -g -O0 -I callgrind -I include ... + +#include + +#define MAX_DEPTH 5 +#define BRANCH_FACTOR 3 +#define FIB_N 25 +#define MAX_NODES 1024 + +typedef struct FractalNode { + long value; + int depth; + unsigned long computed_hash; + struct FractalNode *children[BRANCH_FACTOR]; + int num_children; +} FractalNode; + +// Bump-allocated node pool: avoids the allocator frames a heap tree would leak +// into the profile. Reset at the start of every tree build. +static FractalNode g_pool[MAX_NODES]; +static int g_pool_used; + +static FractalNode *pool_alloc(void) { + FractalNode *node = &g_pool[g_pool_used++]; + node->value = 0; + node->depth = 0; + node->computed_hash = 0; + node->num_children = 0; + return node; +} + +// Deterministic child seed (integer stand-in for the original golden-ratio sine). +static long compute_child_value(long parent_value, int child_index, int depth) { + unsigned long base = (unsigned long)parent_value * 2654435761UL; + unsigned long offset = (unsigned long)(child_index + 1) * (unsigned long)(depth + 1); + return (long)(((base ^ (offset * 40503UL)) % 100UL) + 1UL); +} + +static unsigned long compute_tree_hash(const FractalNode *node) { + unsigned long hash = (unsigned long)node->value; + hash = hash * 31 + (unsigned long)node->depth; + + for (int i = 0; i < node->num_children; i++) { + hash = hash * 31 + compute_tree_hash(node->children[i]); + } + return hash; +} + +static FractalNode *build_fractal(int depth, long seed) { + FractalNode *node = pool_alloc(); + node->value = seed; + node->depth = depth; + + if (depth < MAX_DEPTH) { + node->num_children = BRANCH_FACTOR; + for (int i = 0; i < BRANCH_FACTOR; i++) { + long child_seed = compute_child_value(seed, i, depth); + node->children[i] = build_fractal(depth + 1, child_seed); + } + } + + node->computed_hash = compute_tree_hash(node); + return node; +} + +static long recursive_sum(const FractalNode *node) { + long children_sum = 0; + for (int i = 0; i < node->num_children; i++) { + children_sum += recursive_sum(node->children[i]); + } + return node->value + children_sum; +} + +static long max_path_sum(const FractalNode *node) { + if (node->num_children == 0) { + return node->value; + } + + long max_child_path = 0; + for (int i = 0; i < node->num_children; i++) { + long child_path = max_path_sum(node->children[i]); + if (child_path > max_child_path) { + max_child_path = child_path; + } + } + return node->value + max_child_path; +} + +static int count_nodes(const FractalNode *node) { + int count = 1; + for (int i = 0; i < node->num_children; i++) { + count += count_nodes(node->children[i]); + } + return count; +} + +// Collected leaves land in a shared buffer; the caller resets g_leaf_count. +static long g_leaves[MAX_NODES]; +static int g_leaf_count; + +static void collect_leaves(const FractalNode *node) { + if (node->num_children == 0) { + g_leaves[g_leaf_count++] = node->value; + return; + } + for (int i = 0; i < node->num_children; i++) { + collect_leaves(node->children[i]); + } +} + +static int fibonacci_memo(int n, int *memo) { + if (n <= 1) { + return n; + } + if (memo[n] != -1) { + return memo[n]; + } + + int result = fibonacci_memo(n - 1, memo) + fibonacci_memo(n - 2, memo); + memo[n] = result; + return result; +} + +static long compute_variance(const long *values, int count) { + if (count == 0) { + return 0; + } + + long mean = 0; + for (int i = 0; i < count; i++) { + mean += values[i]; + } + mean /= count; + + long variance = 0; + for (int i = 0; i < count; i++) { + long diff = values[i] - mean; + variance += diff * diff; + } + return variance / count; +} + +static long recursive_path_score(long value, int depth) { + if (depth == 0 || value < 2) { + return value; + } + long reduced = (value * 4) / 5; + return 1 + recursive_path_score(reduced, depth - 1) / 2; +} + +static long compute_complexity_score(int node_count, long variance, long max_path) { + long base_score = (long)node_count * variance; + long path_factor = recursive_path_score(max_path, 5); + return base_score + path_factor; +} + +typedef struct { + long total_sum; + int node_count; + long max_path; + long leaf_variance; + long complexity_score; +} TreeAnalysis; + +static TreeAnalysis analyze_fractal_tree(FractalNode *tree, int analysis_depth) { + long total_sum = recursive_sum(tree); + int node_count = count_nodes(tree); + long max_path = max_path_sum(tree); + + g_leaf_count = 0; + collect_leaves(tree); + long leaf_variance = compute_variance(g_leaves, g_leaf_count); + + TreeAnalysis analysis; + if (analysis_depth > 0) { + TreeAnalysis nested = analyze_fractal_tree(tree, analysis_depth - 1); + analysis.total_sum = total_sum + nested.total_sum / 10; + analysis.node_count = node_count; + analysis.max_path = max_path > nested.max_path ? max_path : nested.max_path; + analysis.leaf_variance = (leaf_variance + nested.leaf_variance) / 2; + analysis.complexity_score = + compute_complexity_score(node_count, leaf_variance, max_path); + return analysis; + } + + analysis.total_sum = total_sum; + analysis.node_count = node_count; + analysis.max_path = max_path; + analysis.leaf_variance = leaf_variance; + analysis.complexity_score = compute_complexity_score(node_count, leaf_variance, max_path); + return analysis; +} + +static long complex_fractal_benchmark(void) { + g_pool_used = 0; + FractalNode *tree = build_fractal(0, 42); + + TreeAnalysis analysis = analyze_fractal_tree(tree, 2); + + int memo[FIB_N + 1]; + for (int i = 0; i <= FIB_N; i++) { + memo[i] = -1; + } + long fib_result = fibonacci_memo(FIB_N, memo); + + long tree_hash = (long)compute_tree_hash(tree); + long tree_metric = analysis.total_sum + (long)analysis.node_count * 10 + analysis.max_path + + analysis.leaf_variance + analysis.complexity_score; + + return (tree_metric + fib_result + tree_hash) % 1000000; +} + +// Deepest frame: this is where instrumentation is turned on, with +// main -> run_benchmark -> warmup -> run_measured already live on the native +// stack but the shadow stack empty. The seeder reconstructs that chain. +static long run_measured(void) { + CALLGRIND_START_INSTRUMENTATION; + CALLGRIND_ZERO_STATS; + + long result = complex_fractal_benchmark(); + + CALLGRIND_STOP_INSTRUMENTATION; + return result; +} + +// Two unmeasured warmup iterations (instrumentation still off) before the +// measured run, like a real benchmark harness. +static long warmup(void) { + volatile long acc = 0; + for (int i = 0; i < 2; i++) { + acc += complex_fractal_benchmark(); + } + (void)acc; + return run_measured(); +} + +static long run_benchmark(void) { + return warmup(); +} + +int main(void) { + volatile long result = run_benchmark(); + (void)result; + return 0; +} diff --git a/callgrind-utils/testdata/fractal.rs b/callgrind-utils/testdata/fractal.rs new file mode 100644 index 000000000..3933626b0 --- /dev/null +++ b/callgrind-utils/testdata/fractal.rs @@ -0,0 +1,330 @@ +// Rust twin of `testdata/fractal.c`: a pure-compute recursive fractal whose +// Callgrind client requests fire several frames deep. +// +// The CALLGRIND_* client requests are inline-asm sequences, so a pure-Rust +// binary can't issue them directly. Instead this fixture links the C +// `clgctl.c` shim (compiled into a static lib by the test harness) and calls +// `clg_start` / `clg_stop` through FFI, the same shim the Python fixture drives +// via ctypes. +// +// Every function is `#[no_mangle]` so the profile carries stable C-like symbol +// names (Callgrind's node redaction does not strip Rust mangling hashes). +// Integer arithmetic and a fixed-size arena (no `Vec`, no `f64`) keep the graph +// free of allocator / libm frames, so the parsed JSON is stable across +// platforms. +// +// Build (done by tests/rust_callgraph.rs): +// rustc --edition 2021 -g -C opt-level=0 -L native= -l static=clgctl ... + +#![allow(dead_code)] + +const MAX_DEPTH: usize = 5; +const BRANCH_FACTOR: usize = 3; +const FIB_N: usize = 25; +const MAX_NODES: usize = 1024; + +extern "C" { + fn clg_start(); + fn clg_stop(); +} + +#[derive(Clone, Copy)] +struct FractalNode { + value: i64, + depth: i64, + computed_hash: u64, + children: [usize; BRANCH_FACTOR], + num_children: usize, +} + +impl FractalNode { + const fn zero() -> Self { + FractalNode { + value: 0, + depth: 0, + computed_hash: 0, + children: [0; BRANCH_FACTOR], + num_children: 0, + } + } +} + +// Bump-allocated node arena: avoids the allocator frames a heap tree would leak +// into the profile. A fresh arena is used for every tree build. +struct Pool { + nodes: [FractalNode; MAX_NODES], + used: usize, +} + +impl Pool { + fn new() -> Self { + Pool { + nodes: [FractalNode::zero(); MAX_NODES], + used: 0, + } + } +} + +#[no_mangle] +#[inline(never)] +fn pool_alloc(pool: &mut Pool) -> usize { + let idx = pool.used; + pool.used += 1; + pool.nodes[idx] = FractalNode::zero(); + idx +} + +// Deterministic child seed (integer stand-in for the original golden-ratio sine). +#[no_mangle] +#[inline(never)] +fn compute_child_value(parent_value: i64, child_index: usize, depth: usize) -> i64 { + let base = (parent_value as u64).wrapping_mul(2654435761); + let offset = ((child_index as u64) + 1).wrapping_mul((depth as u64) + 1); + (((base ^ offset.wrapping_mul(40503)) % 100) + 1) as i64 +} + +#[no_mangle] +#[inline(never)] +fn compute_tree_hash(pool: &Pool, idx: usize) -> u64 { + let node = pool.nodes[idx]; + let mut hash = (node.value as u64).wrapping_mul(31).wrapping_add(node.depth as u64); + for i in 0..node.num_children { + hash = hash + .wrapping_mul(31) + .wrapping_add(compute_tree_hash(pool, node.children[i])); + } + hash +} + +#[no_mangle] +#[inline(never)] +fn build_fractal(pool: &mut Pool, depth: usize, seed: i64) -> usize { + let idx = pool_alloc(pool); + pool.nodes[idx].value = seed; + pool.nodes[idx].depth = depth as i64; + + if depth < MAX_DEPTH { + let mut children = [0usize; BRANCH_FACTOR]; + for i in 0..BRANCH_FACTOR { + let child_seed = compute_child_value(seed, i, depth); + children[i] = build_fractal(pool, depth + 1, child_seed); + } + pool.nodes[idx].children = children; + pool.nodes[idx].num_children = BRANCH_FACTOR; + } + + pool.nodes[idx].computed_hash = compute_tree_hash(pool, idx); + idx +} + +#[no_mangle] +#[inline(never)] +fn recursive_sum(pool: &Pool, idx: usize) -> i64 { + let node = pool.nodes[idx]; + let mut children_sum = 0i64; + for i in 0..node.num_children { + children_sum += recursive_sum(pool, node.children[i]); + } + node.value + children_sum +} + +#[no_mangle] +#[inline(never)] +fn max_path_sum(pool: &Pool, idx: usize) -> i64 { + let node = pool.nodes[idx]; + if node.num_children == 0 { + return node.value; + } + + let mut max_child_path = 0i64; + for i in 0..node.num_children { + let child_path = max_path_sum(pool, node.children[i]); + if child_path > max_child_path { + max_child_path = child_path; + } + } + node.value + max_child_path +} + +#[no_mangle] +#[inline(never)] +fn count_nodes(pool: &Pool, idx: usize) -> i64 { + let node = pool.nodes[idx]; + let mut count = 1i64; + for i in 0..node.num_children { + count += count_nodes(pool, node.children[i]); + } + count +} + +#[no_mangle] +#[inline(never)] +fn collect_leaves(pool: &Pool, idx: usize, leaves: &mut [i64], count: &mut usize) { + let node = pool.nodes[idx]; + if node.num_children == 0 { + leaves[*count] = node.value; + *count += 1; + return; + } + for i in 0..node.num_children { + collect_leaves(pool, node.children[i], leaves, count); + } +} + +#[no_mangle] +#[inline(never)] +fn fibonacci_memo(n: i64, memo: &mut [i64]) -> i64 { + if n <= 1 { + return n; + } + if memo[n as usize] != -1 { + return memo[n as usize]; + } + + let result = fibonacci_memo(n - 1, memo) + fibonacci_memo(n - 2, memo); + memo[n as usize] = result; + result +} + +#[no_mangle] +#[inline(never)] +fn compute_variance(values: &[i64]) -> i64 { + if values.is_empty() { + return 0; + } + + let mut mean = 0i64; + for &v in values { + mean += v; + } + mean /= values.len() as i64; + + let mut variance = 0i64; + for &v in values { + let diff = v - mean; + variance += diff * diff; + } + variance / values.len() as i64 +} + +#[no_mangle] +#[inline(never)] +fn recursive_path_score(value: i64, depth: usize) -> i64 { + if depth == 0 || value < 2 { + return value; + } + let reduced = (value * 4) / 5; + 1 + recursive_path_score(reduced, depth - 1) / 2 +} + +#[no_mangle] +#[inline(never)] +fn compute_complexity_score(node_count: i64, variance: i64, max_path: i64) -> i64 { + let base_score = node_count * variance; + let path_factor = recursive_path_score(max_path, 5); + base_score + path_factor +} + +#[derive(Clone, Copy)] +struct TreeAnalysis { + total_sum: i64, + node_count: i64, + max_path: i64, + leaf_variance: i64, + complexity_score: i64, +} + +#[no_mangle] +#[inline(never)] +fn analyze_fractal_tree(pool: &Pool, root: usize, analysis_depth: usize) -> TreeAnalysis { + let total_sum = recursive_sum(pool, root); + let node_count = count_nodes(pool, root); + let max_path = max_path_sum(pool, root); + + let mut leaves = [0i64; MAX_NODES]; + let mut leaf_count = 0usize; + collect_leaves(pool, root, &mut leaves, &mut leaf_count); + let leaf_variance = compute_variance(&leaves[..leaf_count]); + + if analysis_depth > 0 { + let nested = analyze_fractal_tree(pool, root, analysis_depth - 1); + return TreeAnalysis { + total_sum: total_sum + nested.total_sum / 10, + node_count, + max_path: max_path.max(nested.max_path), + leaf_variance: (leaf_variance + nested.leaf_variance) / 2, + complexity_score: compute_complexity_score(node_count, leaf_variance, max_path), + }; + } + + TreeAnalysis { + total_sum, + node_count, + max_path, + leaf_variance, + complexity_score: compute_complexity_score(node_count, leaf_variance, max_path), + } +} + +#[no_mangle] +#[inline(never)] +fn complex_fractal_benchmark() -> i64 { + let mut pool = Pool::new(); + let root = build_fractal(&mut pool, 0, 42); + + let analysis = analyze_fractal_tree(&pool, root, 2); + + let mut memo = [-1i64; FIB_N + 1]; + let fib_result = fibonacci_memo(FIB_N as i64, &mut memo); + + let tree_hash = compute_tree_hash(&pool, root) as i64; + let tree_metric = analysis.total_sum + + analysis.node_count * 10 + + analysis.max_path + + analysis.leaf_variance + + analysis.complexity_score; + + (tree_metric.wrapping_add(fib_result).wrapping_add(tree_hash)).rem_euclid(1_000_000) +} + +// Deepest frame: instrumentation is turned on here, with +// main -> run_benchmark -> warmup -> run_measured already live on the native +// stack but the shadow stack empty. The seeder reconstructs that chain. +#[no_mangle] +#[inline(never)] +fn run_measured() -> i64 { + unsafe { + clg_start(); + } + + let result = complex_fractal_benchmark(); + + unsafe { + clg_stop(); + } + result +} + +// Two unmeasured warmup iterations (instrumentation still off) before the +// measured run, like a real benchmark harness. +#[no_mangle] +#[inline(never)] +fn warmup() -> i64 { + let mut acc = 0i64; + for _ in 0..2 { + acc = acc.wrapping_add(complex_fractal_benchmark()); + } + std::hint::black_box(acc); + run_measured() +} + +#[no_mangle] +#[inline(never)] +fn run_benchmark() -> i64 { + warmup() +} + +fn main() { + let result = run_benchmark(); + std::hint::black_box(result); +} diff --git a/callgrind-utils/testdata/mutual.c b/callgrind-utils/testdata/mutual.c new file mode 100644 index 000000000..0afc3013f --- /dev/null +++ b/callgrind-utils/testdata/mutual.c @@ -0,0 +1,26 @@ +// Fixture: mutual recursion `is_even <-> is_odd`, forming a two-function cycle +// reached from `main`. Exercises cyclic call topology. See recursion.c for the +// instrumentation/build conventions. + +#include + +static int is_odd(int n); + +static int is_even(int n) { + return n == 0 ? 1 : is_odd(n - 1); +} + +static int is_odd(int n) { + return n == 0 ? 0 : is_even(n - 1); +} + +int main(void) { + CALLGRIND_START_INSTRUMENTATION; + CALLGRIND_ZERO_STATS; + + volatile int sink = is_even(6); + (void)sink; + + CALLGRIND_STOP_INSTRUMENTATION; + return 0; +} diff --git a/callgrind-utils/testdata/recursion.c b/callgrind-utils/testdata/recursion.c new file mode 100644 index 000000000..e27cfa00e --- /dev/null +++ b/callgrind-utils/testdata/recursion.c @@ -0,0 +1,40 @@ +// Fixture for callgrind-utils snapshot tests. +// +// A small, pure-compute call graph: direct recursion (`fib` -> `fib`) plus two +// helper edges (`compute` -> `fib`, `compute` -> `square`) under `main`. +// +// Mirrors how CodSpeed drives a benchmark: instrumentation is off at startup +// (run with `--instr-atstart=no`), so loader/libc-start frames are excluded, +// then turned on around the measured region. Build with `-g -O0` so the +// functions are real (no inlining) and carry debug names. +// +// Requires the in-repo Callgrind client-request header: +// cc -g -O0 -I callgrind -I include ... + +#include + +static int fib(int n) { + if (n < 2) { + return n; + } + return fib(n - 1) + fib(n - 2); +} + +static int square(int n) { + return n * n; +} + +static int compute(int n) { + return fib(n) + square(n); +} + +int main(void) { + CALLGRIND_START_INSTRUMENTATION; + CALLGRIND_ZERO_STATS; + + volatile int sink = compute(8); + (void)sink; + + CALLGRIND_STOP_INSTRUMENTATION; + return 0; +} diff --git a/callgrind-utils/testdata/recursion.py b/callgrind-utils/testdata/recursion.py new file mode 100644 index 000000000..5ea30dde1 --- /dev/null +++ b/callgrind-utils/testdata/recursion.py @@ -0,0 +1,59 @@ +# Python counterpart to recursion.c: the same fib/square/compute shape, driven +# the way CodSpeed drives a benchmark. Instrumentation is off at startup (run +# with --instr-atstart=no) and turned on around the measured region via the +# clgctl shim, whose compiled path is passed as argv[1]. +# +# Before starting, we skip the Python runtime objects (libpython + the python +# executable) from Callgrind at runtime, exactly as pytest-codspeed's +# instrument-hooks does in _callgrind_skip_python_runtime: the interpreter's own +# C frames are folded into their callers so they don't obfuscate the graph. +# Matching is by exact realpath, since Callgrind keys obj-skip on the mapped +# object path. +import ctypes +import os +import sys +import sysconfig + +clgctl = ctypes.CDLL(sys.argv[1]) + + +def skip_python_runtime(): + ldlibrary = sysconfig.get_config_var("LDLIBRARY") + libdir = sysconfig.get_config_var("LIBDIR") + libpython = next( + ( + p + for p in ( + os.path.join(libdir, ldlibrary) if ldlibrary and libdir else None, + os.path.join(sys.prefix, "lib", ldlibrary) if ldlibrary else None, + ) + if p and os.path.exists(p) + ), + None, + ) + for path in (libpython, sys.executable): + if path: + clgctl.clg_add_obj_skip(os.path.realpath(path).encode()) + + +def fib(n): + if n < 2: + return n + return fib(n - 1) + fib(n - 2) + + +def square(n): + return n * n + + +def compute(n): + return fib(n) + square(n) + + +skip_python_runtime() + +clgctl.clg_start() +sink = compute(20) +clgctl.clg_stop() + +assert sink == 7165, sink diff --git a/callgrind-utils/tests/data/example.out b/callgrind-utils/tests/data/example.out new file mode 100644 index 000000000..baa8ab717 --- /dev/null +++ b/callgrind-utils/tests/data/example.out @@ -0,0 +1,125 @@ +# callgrind format +# Runner-style uncompressed output (--compress-strings=no): full fn=/fl=/ob= +# names, blank-line separated function records, a single part: block. Fed +# directly to the monorepo callgrind-parser with no local preprocessing. Every +# callee also has its own fn= record, since build_call_graph only nodes +# functions that appear as fn= and drops edges to otherwise-unknown callees. +version: 1 +creator: callgrind-fixture +pid: 1 +cmd: ./prog +positions: line +events: Ir +part: 1 + +# ===== main: object /path/to/clreq, file file1.c ===== +ob=/path/to/clreq +fl=file1.c +fn=main +0 4 +# regular call: main -> func1; no cfi -> callee inherits file1.c. +cfn=func1 +calls=1 50 +16 400 +# cfl= (the cfi= alias) sets the callee file; here it just names a unique callee. +cfl=cflfile.c +cfn=cflop +calls=1 52 +18 30 +# omitted cfi/cfl: callee inherits the current file context (file1.c). +cfn=nofile +calls=1 53 +19 10 +# same function name in two objects/files -> two distinct nodes. The first call +# uses cfl= (the cfi= alias), the second cfi=; cob= sets the object in both. +# Each must resolve its callee file+object to select the right same-named node. +cob=liba +cfl=fileA.c +cfn=helper +calls=1 60 +20 5 +cob=libb +cfi=fileB.c +cfn=helper +calls=1 61 +21 5 +# cob= overrides the callee object; file inherited from context (file1.c). +cob=extlib +cfn=extfn +calls=1 70 +22 3 + +# ===== func1 ===== +fl=file1.c +fn=func1 +ob=/path/to/clreq +23 100 +cfn=func2 +calls=1 54 +24 50 + +# ===== func2 ===== +fl=file1.c +fn=func2 +ob=/path/to/clreq +25 20 +cfn=rec +calls=1 55 +26 50 +# a function name reused by a later call still yields a distinct edge. +cfn=func1 +calls=1 62 +27 20 + +# ===== rec: direct recursion -> self-edge (caller == callee) ===== +fl=file1.c +fn=rec +ob=/path/to/clreq +28 7 +cfn=rec +calls=1 56 +29 7 + +# ===== callee functions (need their own fn= to become nodes) ===== +fl=cflfile.c +fn=cflop +ob=/path/to/clreq +40 30 + +fl=file1.c +fn=nofile +ob=/path/to/clreq +41 10 + +fl=fileA.c +fn=helper +ob=liba +42 5 + +fl=fileB.c +fn=helper +ob=libb +43 5 + +fl=file1.c +fn=extfn +ob=extlib +44 3 + +fl=inline.c +fn=inltarget +ob=/path/to/clreq +45 4 + +# ===== inlhost: inline fi=/fe= file transition ===== +# fi= moves the current file context to inline.c for the callee's call; fe= +# restores file1.c before the function terminates, so the caller keeps file1.c. +fl=file1.c +fn=inlhost +ob=/path/to/clreq +30 4 +fi=inline.c +cfn=inltarget +calls=1 57 +31 4 +fe=file1.c diff --git a/callgrind-utils/tests/flamegraph.rs b/callgrind-utils/tests/flamegraph.rs new file mode 100644 index 000000000..090b24bab --- /dev/null +++ b/callgrind-utils/tests/flamegraph.rs @@ -0,0 +1,195 @@ +//! Tests for the collapsed-stack / flamegraph projection. + +use callgrind_utils::error::FlamegraphError; +use callgrind_utils::model::CallGraph; +use std::io::Cursor; + +fn parse(out: &str) -> CallGraph { + CallGraph::parse(Cursor::new(out)).expect("parse") +} + +fn folded_sorted(g: &CallGraph) -> Vec { + let mut lines = g.to_folded(); + lines.sort(); + lines +} + +const LINEAR: &str = "\ +part: 1 +pid: 1 +positions: line +events: Ir +fn=main +10 5 +cfn=work +calls=1 90 +11 90 + +fn=work +20 40 +cfn=leaf +calls=1 50 +21 50 + +fn=leaf +30 50 +"; + +#[test] +fn folds_linear_chain_with_self_costs() { + let g = parse(LINEAR); + assert_eq!( + folded_sorted(&g), + vec![ + "main 5".to_string(), + "main;work 40".to_string(), + "main;work;leaf 50".to_string(), + ] + ); +} + +#[test] +fn renders_svg() { + let g = parse(LINEAR); + let svg = g.to_flamegraph().expect("svg"); + assert!(svg.contains("().unwrap()) + .sum(); + assert_eq!(total, 105, "entry(5) + hot(100)"); +} + +const SPARSE: &str = "\ +part: 1 +pid: 1 +positions: instr line +events: Ir Dr Dw +fn=main +0x1000 10 7 ++4 11 3 0 +cfn=leaf +calls=1 0 0 +0x2000 12 20 + +fn=leaf +0x3000 20 20 0 0 +"; + +#[test] +fn parses_sparse_instr_line_cost_lines() { + let g = parse(SPARSE); + assert_eq!( + folded_sorted(&g), + vec!["main 10".to_string(), "main;leaf 20".to_string()], + "self=7+3 for main, inclusive/self=20 for leaf" + ); +} + +#[test] +fn no_cost_data_is_an_error() { + let out = "\ +part: 1 +pid: 1 +positions: line +events: Ir +fn=main +0 0 +cfn=child +calls=1 0 +0 0 + +fn=child +0 0 +"; + let g = parse(out); + assert!(matches!(g.to_flamegraph(), Err(FlamegraphError::NoCost))); +} diff --git a/callgrind-utils/tests/parser.rs b/callgrind-utils/tests/parser.rs new file mode 100644 index 000000000..4f2ed4bc7 --- /dev/null +++ b/callgrind-utils/tests/parser.rs @@ -0,0 +1,228 @@ +//! Integration tests for the Callgrind `.out` -> call-graph parser. +//! +//! The fixture (`data/example.out`) uses runner-style uncompressed Callgrind +//! syntax (`--compress-strings=no`): full `fn=`/`fl=`/`ob=` names, blank-line +//! separated function records, and a single `part:` block, fed directly to the +//! monorepo `callgrind-parser` with no local preprocessing. Every callee also +//! has its own `fn=` record, since `build_call_graph` only nodes functions that +//! appear as `fn=` and drops edges to otherwise-unknown callees. +//! +//! Topology is asserted through the canonical JSON projection (`to_json`), +//! which exposes node identity (`name`/`object`/`file`) and indexed edges with +//! per-thread call counts. Because the parser falls back to matching a call by +//! function name alone when the full `(name, file, object)` id misses, the +//! `cfl`/`cfi`/`cob` directives are only observable for *same-named* callees: +//! the `helper` pair is called once via `cfl=` and once via `cfi=` (with `cob=` +//! on both), and the tests pin which distinct node each edge targets. Path +//! normalization is exercised through `redact`, which basenames `file`/`object` +//! before serialization. + +use callgrind_utils::model::CallGraph; +use serde_json::Value; +use std::io::Cursor; + +const FIXTURE: &str = include_str!("data/example.out"); + +fn parse_default() -> CallGraph { + CallGraph::parse(Cursor::new(FIXTURE)).expect("parse fixture") +} + +fn json(g: &CallGraph) -> Value { + serde_json::from_str(&g.to_json().expect("to_json")).expect("valid json") +} + +/// The first node named `name` (unique names only). +fn node<'a>(v: &'a Value, name: &str) -> &'a Value { + v["nodes"] + .as_array() + .expect("nodes array") + .iter() + .find(|n| n["name"].as_str() == Some(name)) + .unwrap_or_else(|| panic!("node {name} not found")) +} + +/// All nodes named `name` (distinct by object/file). +fn nodes_named<'a>(v: &'a Value, name: &str) -> Vec<&'a Value> { + v["nodes"] + .as_array() + .expect("nodes array") + .iter() + .filter(|n| n["name"].as_str() == Some(name)) + .collect() +} + +/// The node an edge points at. +fn edge_target<'a>(v: &'a Value, e: &Value) -> &'a Value { + let idx = e["target"].as_u64().unwrap() as usize; + &v["nodes"].as_array().expect("nodes array")[idx] +} + +/// All edges whose source/target nodes (looked up by name) are `caller`/`callee`. +fn edges_between<'a>(v: &'a Value, caller: &str, callee: &str) -> Vec<&'a Value> { + let names: Vec<&str> = v["nodes"] + .as_array() + .expect("nodes array") + .iter() + .map(|n| n["name"].as_str().unwrap()) + .collect(); + v["edges"] + .as_array() + .expect("edges array") + .iter() + .filter(|e| { + let s = e["source"].as_u64().unwrap() as usize; + let t = e["target"].as_u64().unwrap() as usize; + names.get(s).copied() == Some(caller) && names.get(t).copied() == Some(callee) + }) + .collect() +} + +/// Call count of an edge: `counts` is `[[{pid,tid}, count], ...]`; take the +/// single (pid,tid) entry the fixture produces. +fn call_count(e: &Value) -> u64 { + e["counts"] + .as_array() + .expect("counts array") + .first() + .expect("at least one count entry") + .as_array() + .expect("count pair")[1] + .as_u64() + .unwrap() +} + +#[test] +fn parses_basic_callgraph() { + let v = json(&parse_default()); + assert_eq!(v["nodes"].as_array().unwrap().len(), 11, "nodes: {v:#?}"); + assert_eq!(v["edges"].as_array().unwrap().len(), 11, "edges: {v:#?}"); + + let e = &edges_between(&v, "main", "func1"); + assert_eq!(e.len(), 1); + assert_eq!(call_count(e[0]), 1); + // No cfi on this call -> callee inherits the caller's file. + assert_eq!(node(&v, "main")["file"].as_str(), Some("file1.c")); + assert_eq!(node(&v, "func1")["file"].as_str(), Some("file1.c")); +} + +#[test] +fn node_identity_carries_file_and_object() { + // A function's node identity is its (name, object, file) from the fl=/ob= + // records, so two unrelated callees keep distinct, verbatim identities. + let v = json(&parse_default()); + let cflop = node(&v, "cflop"); + assert_eq!(cflop["file"].as_str(), Some("cflfile.c")); + assert_eq!(cflop["object"].as_str(), Some("/path/to/clreq")); + let extfn = node(&v, "extfn"); + assert_eq!(extfn["object"].as_str(), Some("extlib")); + assert_eq!(extfn["file"].as_str(), Some("file1.c")); +} + +#[test] +fn same_name_callees_selected_by_cfl_cfi_and_cob() { + // `helper` is defined in liba/fileA.c and libb/fileB.c. main calls it once + // via cfl=+cob= and once via cfi=+cob=. Each call must resolve its callee + // file+object to target the *correct* same-named node; if a directive were + // dropped the parser's name-only fallback would target the wrong one. + let v = json(&parse_default()); + assert_eq!(nodes_named(&v, "helper").len(), 2); + + let mut targets: Vec<(&str, &str)> = edges_between(&v, "main", "helper") + .iter() + .map(|e| { + let t = edge_target(&v, e); + (t["object"].as_str().unwrap(), t["file"].as_str().unwrap()) + }) + .collect(); + targets.sort(); + assert_eq!( + targets, + vec![("liba", "fileA.c"), ("libb", "fileB.c")], + "cfl=/cfi=/cob= must each select the right same-named callee" + ); +} + +#[test] +fn inline_fe_restores_caller_file() { + // fi=inline.c moves the current file context for the callee's call; fe= + // restores file1.c before the function terminates, so the caller (inlhost) + // keeps file1.c rather than the inline file. + let v = json(&parse_default()); + assert_eq!(node(&v, "inlhost")["file"].as_str(), Some("file1.c")); + assert_eq!(node(&v, "inltarget")["file"].as_str(), Some("inline.c")); + assert_eq!(edges_between(&v, "inlhost", "inltarget").len(), 1); +} + +#[test] +fn recursion_becomes_self_edge() { + let v = json(&parse_default()); + let rec = edges_between(&v, "rec", "rec"); + assert_eq!(rec.len(), 1); + assert_eq!(rec[0]["source"], rec[0]["target"]); +} + +#[test] +fn redact_basenames_object_and_file_paths() { + // Raw JSON keeps the full object path verbatim; `redact` basenames it. + let raw = json(&parse_default()); + assert!( + raw["nodes"] + .as_array() + .unwrap() + .iter() + .any(|n| n["object"].as_str() == Some("/path/to/clreq")) + ); + + let redacted = json(&parse_default().redact()); + assert!( + redacted["nodes"] + .as_array() + .unwrap() + .iter() + .any(|n| n["object"].as_str() == Some("clreq")) + ); + assert!( + !redacted["nodes"] + .as_array() + .unwrap() + .iter() + .any(|n| n["object"].as_str().unwrap().contains('/')), + "no object should retain a path separator after redaction" + ); +} + +#[test] +fn to_json_is_canonical() { + let g = parse_default(); + let v = json(&g); + + assert_eq!(v["version"].as_u64(), Some(3)); + let nodes = v["nodes"].as_array().unwrap(); + let edges = v["edges"].as_array().unwrap(); + assert_eq!(nodes.len(), 11); + assert_eq!(edges.len(), 11); + + // Edges reference valid node indices. + for e in edges { + let s = e["source"].as_u64().unwrap() as usize; + let t = e["target"].as_u64().unwrap() as usize; + assert!( + s < nodes.len() && t < nodes.len(), + "edge index out of range" + ); + } + + // Edges are sorted by source index (the serializer's canonical order). + let sources: Vec = edges + .iter() + .map(|e| e["source"].as_u64().unwrap()) + .collect(); + let mut sorted = sources.clone(); + sorted.sort(); + assert_eq!(sources, sorted, "edges must be sorted by source"); + + // Output is deterministic: re-parsing yields byte-identical JSON. + let j1 = parse_default().to_json().unwrap(); + let j2 = parse_default().to_json().unwrap(); + assert_eq!(j1, j2, "JSON must be deterministic across parses"); +} diff --git a/callgrind-utils/tests/python_callgraph.rs b/callgrind-utils/tests/python_callgraph.rs new file mode 100644 index 000000000..a118b6ea9 --- /dev/null +++ b/callgrind-utils/tests/python_callgraph.rs @@ -0,0 +1,155 @@ +//! Snapshot of the Python fixture's external Callgrind graph JSON. +//! +//! Callgrind records the CPython interpreter's C frames, not the Python +//! functions: the interpreter loop is obj-skipped at runtime via the `clgctl` +//! shim's `CALLGRIND_ADD_OBJ_SKIP`, so what remains is the ctypes/libffi/libc +//! C-residual around the `clg_start`/`clg_stop` shim. +//! +//! Requires a built `./vg-in-place` at the repo root and `cc`. Silently skips +//! when `python3` is not on PATH (mirrors the `.vgtest` `prereq` guards). +use std::env::consts::ARCH; +use std::io::Cursor; +use std::path::{Path, PathBuf}; +use std::process::Command; + +use callgrind_utils::model::CallGraph; + +/// Repo root: this crate lives at `/callgrind-utils`. +fn repo_root() -> PathBuf { + Path::new(env!("CARGO_MANIFEST_DIR")) + .parent() + .expect("crate has a parent directory") + .to_path_buf() +} + +fn vg_in_place() -> PathBuf { + let path = repo_root().join("vg-in-place"); + assert!( + path.is_file(), + "vg-in-place not found at {} - build Valgrind in place first", + path.display() + ); + path +} + +fn have_python3() -> bool { + Command::new("python3") + .arg("--version") + .output() + .map(|o| o.status.success()) + .unwrap_or(false) +} + +/// Compile the Callgrind client-request shim the Python fixture loads via +/// `ctypes`, as a shared library against the in-repo `callgrind.h`. +fn compile_clgctl() -> PathBuf { + let repo = repo_root(); + let src = Path::new(env!("CARGO_MANIFEST_DIR")).join("testdata/clgctl.c"); + let lib = Path::new(env!("CARGO_TARGET_TMPDIR")).join("libclgctl.so"); + + let status = Command::new("cc") + .args(["-g", "-O0", "-shared", "-fPIC"]) + .arg("-I") + .arg(repo.join("callgrind")) + .arg("-I") + .arg(repo.join("include")) + .arg("-o") + .arg(&lib) + .arg(&src) + .status() + .unwrap_or_else(|e| panic!("failed to spawn cc for clgctl: {e}")); + assert!( + status.success(), + "cc failed for {} ({status})", + src.display() + ); + lib +} + +fn runner_callgrind_args(out_file: &Path) -> Vec { + [ + "-q", + "--trace-children=yes", + "--cache-sim=yes", + "--I1=32768,8,64", + "--D1=32768,8,64", + "--LL=8388608,16,64", + "--instr-atstart=no", + "--collect-systime=nsec", + "--read-inline-info=yes", + "--tool=callgrind", + "--compress-strings=no", + "--combine-dumps=yes", + "--dump-line=no", + ] + .into_iter() + .map(str::to_string) + .chain([format!("--callgrind-out-file={}", out_file.display())]) + .collect() +} + +/// Profile `testdata/recursion.py` with the same Callgrind flags as the runner +/// and return the `.out` contents. +fn run_python(clgctl: &Path, _instr_atstart: bool) -> String { + let script = Path::new(env!("CARGO_MANIFEST_DIR")).join("testdata/recursion.py"); + let out_file = Path::new(env!("CARGO_TARGET_TMPDIR")).join("python.callgrind.out"); + let log_file = out_file.with_extension("valgrind.log"); + + let status = Command::new("setarch") + .arg(ARCH) + .arg("--addr-no-randomize") + .arg(vg_in_place()) + .args(runner_callgrind_args(&out_file)) + .arg(format!("--log-file={}", log_file.display())) + .arg("python3") + .arg(&script) + .arg(clgctl) + .status() + .unwrap_or_else(|e| panic!("failed to spawn setarch/vg-in-place: {e}")); + assert!(status.success(), "vg-in-place exited with {status}"); + std::fs::read_to_string(&out_file) + .unwrap_or_else(|e| panic!("read {}: {e}", out_file.display())) +} + +#[test] +#[ignore] +fn python_topology_json() { + if !have_python3() { + eprintln!("skipping python_topology_json: python3 not on PATH"); + return; + } + + let clgctl = compile_clgctl(); + let raw = run_python(&clgctl, false); + let graph = CallGraph::parse(Cursor::new(raw.as_str())) + .unwrap_or_else(|e| panic!("parse python callgrind output: {e:?}")) + .redact(); + let json = graph.to_json().expect("serialize"); + + insta::assert_snapshot!("recursion_py__callgrind_json", json); +} + +/// Render a flamegraph of the fixture profiled with `--instr-atstart=yes`, so +/// the whole-program call stack is captured from process start and the +/// interpreter's `fib` recursion (`_PyEval_EvalFrameDefault` and the +/// PyLong/frame helpers) is visible. Under `--instr-atstart=no` the measured +/// region begins inside already-obj-skipped libpython, so everything folds +/// into `(below main)` and the flamegraph is a single uninformative bar. +/// Rendered from the RAW graph (redaction collapses libc/ld into a non-root +/// `???` node). Writes `python.svg` at the crate root for manual inspection. +#[test] +#[ignore] +fn python_flamegraph() { + if !have_python3() { + eprintln!("skipping python_flamegraph: python3 not on PATH"); + return; + } + + let clgctl = compile_clgctl(); + let raw = run_python(&clgctl, true); + let graph = CallGraph::parse(Cursor::new(raw.as_str())) + .unwrap_or_else(|e| panic!("parse python callgrind output: {e:?}")); + + let out = Path::new(env!("CARGO_MANIFEST_DIR")).join("python.svg"); + graph.to_flamegraph_file(&out).expect("render flamegraph"); +} diff --git a/callgrind-utils/tests/rust_callgraph.rs b/callgrind-utils/tests/rust_callgraph.rs new file mode 100644 index 000000000..c81618fd4 --- /dev/null +++ b/callgrind-utils/tests/rust_callgraph.rs @@ -0,0 +1,218 @@ +//! Golden snapshot of the Rust fixture's call graph. +//! +//! The Rust twin of the C `fractal` case in `snapshot.rs`: compile +//! `testdata/fractal.rs` (linking the `clgctl.c` client-request shim as a static +//! lib, since the CALLGRIND_* requests are inline asm), profile it live under +//! the in-repo Callgrind with `--instr-atstart=no`, parse, and snapshot the +//! redacted canonical JSON. +//! +//! The fixture fires the client requests several frames deep +//! (`main` -> `run_benchmark` -> `warmup` -> `run_measured`), so the scoped +//! graph is just the measured region's own functions: the shadow-stack seeder +//! reconstructs the native chain but the outer frames do their work while +//! instrumentation is off, so they never enter the graph. Every fixture +//! function is `#[no_mangle] #[inline(never)]` and the workload is pure integer +//! math over a fixed arena, so the only non-fixture frame is a libc `memset` +//! (redacted to `???`) and the JSON is stable across platforms. +//! +//! A second `--instr-atstart=yes` case captures the whole program from process +//! start, mirroring the C `fixture_full_trace`: the std runtime startup +//! (`std::rt::lang_start`), `main`, and the loader frames appear, and the JSON +//! is snapshotted raw (no redaction), so it is toolchain- and platform-specific +//! like the C full-trace snapshots. Callgrind demangles the Rust symbols and +//! drops their hash suffixes, so the names stay stable for a pinned toolchain. +//! +//! Requires a built `./vg-in-place` at the repo root. Silently skips when +//! `rustc` is not on PATH. +use std::env::consts::ARCH; +use std::io::Cursor; +use std::path::{Path, PathBuf}; +use std::process::Command; + +use callgrind_utils::model::CallGraph; + +/// Repo root: this crate lives at `/callgrind-utils`. +fn repo_root() -> PathBuf { + Path::new(env!("CARGO_MANIFEST_DIR")) + .parent() + .expect("crate has a parent directory") + .to_path_buf() +} + +fn vg_in_place() -> PathBuf { + let path = repo_root().join("vg-in-place"); + assert!( + path.is_file(), + "vg-in-place not found at {} - build Valgrind in place first", + path.display() + ); + path +} + +fn have_rustc() -> bool { + Command::new("rustc") + .arg("--version") + .output() + .map(|o| o.status.success()) + .unwrap_or(false) +} + +/// Compile the Callgrind client-request shim into a static library, then build +/// `testdata/fractal.rs` against it. +/// +/// `-C opt-level=2` inlines away the std iterator / bounds-check helpers so they +/// don't appear as their own (toolchain-version-specific) nodes; the fixture's +/// own functions stay distinct because each is `#[inline(never)]`. The binary is +/// named `fractal_rs` so its object basename is stable in the snapshot. +/// +/// Each caller passes a private `work` dir: the two test cases run in parallel, +/// so they must not share the intermediate `.o`/`.a`/binary paths. The binary +/// basename stays `fractal_rs` either way, so the snapshot's object name is +/// identical across cases. +fn compile_rust_fixture(work: &Path) -> PathBuf { + let repo = repo_root(); + let manifest = Path::new(env!("CARGO_MANIFEST_DIR")); + let tmp = work; + std::fs::create_dir_all(tmp).expect("create work dir"); + + let obj = tmp.join("clgctl_rs.o"); + let status = Command::new("cc") + .args(["-g", "-O0", "-fPIC", "-c"]) + .arg("-I") + .arg(repo.join("callgrind")) + .arg("-I") + .arg(repo.join("include")) + .arg("-o") + .arg(&obj) + .arg(manifest.join("testdata/clgctl.c")) + .status() + .unwrap_or_else(|e| panic!("failed to spawn cc for clgctl: {e}")); + assert!(status.success(), "cc failed for clgctl.c ({status})"); + + // `ar` appends, so start from a clean archive. + let lib = tmp.join("libclgctl_rs.a"); + let _ = std::fs::remove_file(&lib); + let status = Command::new("ar") + .arg("rcs") + .arg(&lib) + .arg(&obj) + .status() + .unwrap_or_else(|e| panic!("failed to spawn ar: {e}")); + assert!(status.success(), "ar failed ({status})"); + + let bin = tmp.join("fractal_rs"); + let status = Command::new("rustc") + .args(["--edition", "2021", "-g", "-C", "opt-level=2"]) + .arg("-L") + .arg(format!("native={}", tmp.display())) + .arg("-l") + .arg("static=clgctl_rs") + .arg("-o") + .arg(&bin) + .arg(manifest.join("testdata/fractal.rs")) + .status() + .unwrap_or_else(|e| panic!("failed to spawn rustc: {e}")); + assert!(status.success(), "rustc failed ({status})"); + bin +} + +fn runner_callgrind_args(instr_atstart: bool, out_file: &Path) -> Vec { + [ + "-q", + "--trace-children=yes", + "--cache-sim=yes", + "--I1=32768,8,64", + "--D1=32768,8,64", + "--LL=8388608,16,64", + if instr_atstart { + "--instr-atstart=yes" + } else { + "--instr-atstart=no" + }, + "--collect-systime=nsec", + "--read-inline-info=yes", + "--tool=callgrind", + "--compress-strings=no", + "--combine-dumps=yes", + "--dump-line=no", + ] + .into_iter() + .map(str::to_string) + .chain([format!("--callgrind-out-file={}", out_file.display())]) + .collect() +} + +fn run_callgrind_with_runner_args(bin: &Path, out_file: &Path, instr_atstart: bool) -> String { + let status = Command::new("setarch") + .arg(ARCH) + .arg("--addr-no-randomize") + .arg(vg_in_place()) + .args(runner_callgrind_args(instr_atstart, out_file)) + .arg(bin) + .status() + .unwrap_or_else(|e| panic!("failed to spawn setarch/vg-in-place: {e}")); + assert!(status.success(), "vg-in-place exited with {status}"); + std::fs::read_to_string(out_file).unwrap_or_else(|e| panic!("read {}: {e}", out_file.display())) +} + +/// Profile `bin` with the same Callgrind flags as the runner and return the +/// `.out` contents. `--instr-atstart=no` pairs with the fixture's client +/// requests so only the measured region is profiled. +fn run_callgrind(bin: &Path) -> String { + let out_file = bin.with_extension("callgrind.out"); + run_callgrind_with_runner_args(bin, &out_file, false) +} + +/// Profile `bin` with the runner-equivalent Callgrind flags and return the raw, +/// unredacted graph input. This intentionally keeps `--instr-atstart=no`; the +/// production runner does not capture a separate full-program trace. +fn run_callgrind_full(bin: &Path) -> String { + let out_file = bin.with_extension("full.callgrind.out"); + run_callgrind_with_runner_args(bin, &out_file, false) +} + +#[test] +fn rust_fixture_canonical_json() { + if !have_rustc() { + eprintln!("skipping rust_fixture_canonical_json: rustc not on PATH"); + return; + } + + let work = Path::new(env!("CARGO_TARGET_TMPDIR")).join("scoped"); + let bin = compile_rust_fixture(&work); + let raw = run_callgrind(&bin); + let graph = CallGraph::parse(Cursor::new(raw.as_str())) + .unwrap_or_else(|e| panic!("parse rust callgrind output: {e:?}")) + .redact(); + insta::assert_snapshot!( + format!("fractal_rs_folded"), + graph.to_folded_without_costs().join("\n") + ); + graph.to_flamegraph_file("fractal_rs.partial.svg").unwrap(); + let json = graph.to_json().expect("to_json"); + + insta::assert_snapshot!("fractal_rs__json", json); +} + +#[test] +fn rust_fixture_full_trace() { + if !have_rustc() { + eprintln!("skipping rust_fixture_full_trace: rustc not on PATH"); + return; + } + + let work = Path::new(env!("CARGO_TARGET_TMPDIR")).join("full"); + let bin = compile_rust_fixture(&work); + let raw = run_callgrind_full(&bin); + let graph = CallGraph::parse(Cursor::new(raw.as_str())) + .unwrap_or_else(|e| panic!("parse rust full callgrind output: {e:?}")); + + insta::assert_snapshot!( + format!("fractal_rs_full_folded"), + graph.to_folded_without_costs().join("\n") + ); + graph.to_flamegraph_file("fractal_rs.full.svg").unwrap(); + let json = graph.redact().to_json().expect("to_json"); + + insta::assert_snapshot!("fractal_rs_full__json", json); +} diff --git a/callgrind-utils/tests/snapshot.rs b/callgrind-utils/tests/snapshot.rs new file mode 100644 index 000000000..d5d896586 --- /dev/null +++ b/callgrind-utils/tests/snapshot.rs @@ -0,0 +1,165 @@ +//! Golden snapshot tests over the `testdata/*.c` fixtures. +//! +//! Each case compiles its fixture and profiles it with the in-repo Callgrind +//! (`vg-in-place`, expected at the repo root), then snapshots the parsed +//! canonical JSON. The fixtures run with +//! `--instr-atstart=no` (plus client requests) and `--obj-skip`, so the graph +//! is just their own functions and the JSON is stable across platforms. +//! +//! These tests require a built `./vg-in-place` at the repo root. +use std::env::consts::ARCH; +use std::io::Cursor; +use std::path::{Path, PathBuf}; +use std::process::Command; + +use callgrind_utils::model::CallGraph; +use rstest::rstest; + +/// Repo root: this crate lives at `/callgrind-utils`. +fn repo_root() -> PathBuf { + Path::new(env!("CARGO_MANIFEST_DIR")) + .parent() + .expect("crate has a parent directory") + .to_path_buf() +} + +fn vg_in_place() -> PathBuf { + let path = repo_root().join("vg-in-place"); + assert!( + path.is_file(), + "vg-in-place not found at {} - build Valgrind in place first", + path.display() + ); + path +} + +/// Compile `testdata/.c` into this test binary's temp dir. `-O0` keeps the +/// functions un-inlined and `-g` gives them debug names; `callgrind.h` pulls in +/// `valgrind.h` via `-I include`. +fn compile_fixture(stem: &str) -> PathBuf { + let repo = repo_root(); + let src = Path::new(env!("CARGO_MANIFEST_DIR")) + .join("testdata") + .join(format!("{stem}.c")); + let bin = Path::new(env!("CARGO_TARGET_TMPDIR")).join(stem); + + let status = Command::new("cc") + .args(["-g", "-O0"]) + .arg("-I") + .arg(repo.join("callgrind")) + .arg("-I") + .arg(repo.join("include")) + .arg("-o") + .arg(&bin) + .arg(&src) + .status() + .unwrap_or_else(|e| panic!("failed to spawn cc for {stem}: {e}")); + assert!( + status.success(), + "cc failed for {} ({status})", + src.display() + ); + bin +} + +fn runner_callgrind_args(out_file: &Path) -> Vec { + [ + "-q", + "--trace-children=yes", + "--cache-sim=yes", + "--I1=32768,8,64", + "--D1=32768,8,64", + "--LL=8388608,16,64", + "--instr-atstart=no", + "--collect-systime=nsec", + "--read-inline-info=yes", + "--tool=callgrind", + "--compress-strings=no", + "--combine-dumps=yes", + "--dump-line=no", + ] + .into_iter() + .map(str::to_string) + .chain([format!("--callgrind-out-file={}", out_file.display())]) + .collect() +} + +fn run_callgrind_with_runner_args(bin: &Path, out_file: &Path) -> String { + let log_file = out_file.with_extension("valgrind.log"); + let status = Command::new("setarch") + .arg(ARCH) + .arg("--addr-no-randomize") + .arg(vg_in_place()) + .args(runner_callgrind_args(out_file)) + .arg(format!("--log-file={}", log_file.display())) + .arg(bin) + .status() + .unwrap_or_else(|e| panic!("failed to spawn setarch/vg-in-place: {e}")); + assert!(status.success()); + std::fs::read_to_string(out_file).unwrap_or_else(|e| panic!("read {}: {e}", out_file.display())) +} + +/// Profile `bin` with the same Callgrind flags as the runner and return the +/// `.out` contents. +fn run_callgrind(bin: &Path) -> String { + let out_file = bin.with_extension("callgrind.out"); + run_callgrind_with_runner_args(bin, &out_file) +} + +#[rstest] +#[case("recursion")] +#[case("chain")] +#[case("diamond")] +#[case("mutual")] +#[case("fractal")] +fn fixture_canonical_json(#[case] stem: &str) { + let bin = compile_fixture(stem); + let raw = run_callgrind(&bin); + let graph = CallGraph::parse(Cursor::new(raw.as_str())) + .unwrap_or_else(|e| panic!("parse {stem} callgrind output: {e:?}")) + .redact(); + graph + .to_flamegraph_file(format!("{stem}.partial.svg")) + .unwrap(); + insta::assert_snapshot!( + format!("{stem}_folded"), + graph.to_folded_without_costs().join("\n") + ); + + let json = graph.to_json().expect("to_json"); + + insta::assert_snapshot!(format!("{stem}__json"), json); +} + +/// Profile `bin` with the same Callgrind flags as the runner and return the +/// raw, unredacted graph input. The production runner uses +/// `--instr-atstart=no`, so this intentionally does not capture a separate +/// full-program trace. +fn run_callgrind_full(bin: &Path) -> String { + let out_file = bin.with_extension("full.callgrind.out"); + run_callgrind_with_runner_args(bin, &out_file) +} + +#[rstest] +#[case("recursion")] +#[case("chain")] +#[case("diamond")] +#[case("mutual")] +#[case("fractal")] +fn fixture_full_trace(#[case] stem: &str) { + let bin = compile_fixture(stem); + let raw = run_callgrind_full(&bin); + let graph = CallGraph::parse(Cursor::new(raw.as_str())) + .unwrap_or_else(|e| panic!("parse {stem} full callgrind output: {e:?}")); + graph + .to_flamegraph_file(format!("{stem}.full.svg")) + .unwrap(); + + insta::assert_snapshot!( + format!("{stem}_full_folded"), + graph.to_folded_without_costs().join("\n") + ); + + let json = graph.redact().to_json().expect("to_json"); + insta::assert_snapshot!(format!("{stem}_full__json"), json); +} diff --git a/callgrind-utils/tests/snapshots/python_callgraph__recursion_py__topology_json.snap b/callgrind-utils/tests/snapshots/python_callgraph__recursion_py__topology_json.snap new file mode 100644 index 000000000..3deca4311 --- /dev/null +++ b/callgrind-utils/tests/snapshots/python_callgraph__recursion_py__topology_json.snap @@ -0,0 +1,101 @@ +--- +source: tests/python_callgraph.rs +expression: json +--- +{ + "nodes": [ + { + "function": "", + "file": "???", + "object": "_ctypes.cpython.so" + }, + { + "function": "???", + "file": "???", + "object": "ld-linux" + }, + { + "function": "???", + "file": "???", + "object": "libc.so.6" + }, + { + "function": "clg_start", + "file": "clgctl.c", + "object": "libclgctl.so" + }, + { + "function": "", + "file": "???", + "object": "libffi.so" + }, + { + "function": "ffi_call", + "file": "???", + "object": "libffi.so" + }, + { + "function": "ffi_prep_cif", + "file": "???", + "object": "libffi.so" + } + ], + "edges": [ + { + "caller": 0, + "callee": 0 + }, + { + "caller": 0, + "callee": 2 + }, + { + "caller": 0, + "callee": 5 + }, + { + "caller": 0, + "callee": 6 + }, + { + "caller": 1, + "callee": 1 + }, + { + "caller": 1, + "callee": 2 + }, + { + "caller": 2, + "callee": 0 + }, + { + "caller": 2, + "callee": 1 + }, + { + "caller": 2, + "callee": 2 + }, + { + "caller": 3, + "callee": 4 + }, + { + "caller": 4, + "callee": 0 + }, + { + "caller": 4, + "callee": 2 + }, + { + "caller": 4, + "callee": 4 + }, + { + "caller": 5, + "callee": 4 + } + ] +} diff --git a/callgrind-utils/tests/snapshots/rust_callgraph__fractal_rs__json.snap b/callgrind-utils/tests/snapshots/rust_callgraph__fractal_rs__json.snap new file mode 100644 index 000000000..10b0d2da8 --- /dev/null +++ b/callgrind-utils/tests/snapshots/rust_callgraph__fractal_rs__json.snap @@ -0,0 +1,2456 @@ +--- +source: tests/rust_callgraph.rs +expression: json +--- +{ + "edges": [ + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 1, + "target": 35, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 1, + "target": 26, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 1, + "target": 30, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 1, + "target": 0, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 1, + "target": 8, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 1, + "target": 2, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 1, + "target": 22, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 1, + "target": 4, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 1, + "target": 3, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 1, + "target": 19, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 2 + ] + ], + "source": 4, + "target": 35, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 2 + ] + ], + "source": 4, + "target": 26, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 2 + ] + ], + "source": 4, + "target": 30, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 2 + ] + ], + "source": 4, + "target": 0, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 2 + ] + ], + "source": 4, + "target": 8, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 4, + "target": 2, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 2 + ] + ], + "source": 4, + "target": 22, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 4, + "target": 4, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 4, + "target": 3, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 4, + "target": 19, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 4, + "target": 19, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 5, + "target": 32, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 5, + "target": 17, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 5, + "target": 6, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 5, + "target": 17, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 5, + "target": 6, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 5, + "target": 17, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 5, + "target": 6, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 5, + "target": 20, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 363 + ] + ], + "source": 6, + "target": 32, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 120 + ] + ], + "source": 6, + "target": 17, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 120 + ] + ], + "source": 6, + "target": 6, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 120 + ] + ], + "source": 6, + "target": 17, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 120 + ] + ], + "source": 6, + "target": 6, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 120 + ] + ], + "source": 6, + "target": 17, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 120 + ] + ], + "source": 6, + "target": 6, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 120 + ] + ], + "source": 6, + "target": 20, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 243 + ] + ], + "source": 6, + "target": 20, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 7, + "target": 37, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 3 + ] + ], + "source": 8, + "target": 12, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 8, + "target": 9, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 3 + ] + ], + "source": 8, + "target": 12, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 8, + "target": 10, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 3 + ] + ], + "source": 8, + "target": 12, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 8, + "target": 11, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 360 + ] + ], + "source": 12, + "target": 12, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 12, + "target": 9, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 360 + ] + ], + "source": 12, + "target": 12, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 12, + "target": 10, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 360 + ] + ], + "source": 12, + "target": 12, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 12, + "target": 11, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 13, + "target": 0, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 13, + "target": 14, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 13, + "target": 5, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 13, + "target": 1, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 13, + "target": 28, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 13, + "target": 20, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 13, + "target": 15, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 13, + "target": 16, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 17, + "target": 18, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 17, + "target": 18, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 3 + ] + ], + "source": 19, + "target": 33, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 20, + "target": 18, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 20, + "target": 15, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 20, + "target": 10, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 20, + "target": 18, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 122 + ] + ], + "source": 20, + "target": 21, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 20, + "target": 15, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 20, + "target": 11, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 20, + "target": 18, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 122 + ] + ], + "source": 20, + "target": 21, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 20, + "target": 15, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 20, + "target": 10, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 122 + ] + ], + "source": 20, + "target": 21, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 20, + "target": 9, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 21, + "target": 18, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 21, + "target": 15, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 21, + "target": 10, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 21, + "target": 18, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 546 + ] + ], + "source": 21, + "target": 21, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 21, + "target": 15, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 21, + "target": 11, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 21, + "target": 18, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 546 + ] + ], + "source": 21, + "target": 21, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 21, + "target": 15, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 21, + "target": 10, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 546 + ] + ], + "source": 21, + "target": 21, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 21, + "target": 9, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 22, + "target": 23, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 22, + "target": 24, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 22, + "target": 24, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 22, + "target": 24, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 22, + "target": 24, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 22, + "target": 25, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 22, + "target": 24, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 22, + "target": 25, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 22, + "target": 24, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 22, + "target": 24, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 22, + "target": 24, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 22, + "target": 24, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 22, + "target": 24, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 22, + "target": 24, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 22, + "target": 24, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 26, + "target": 11, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 3 + ] + ], + "source": 26, + "target": 27, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 26, + "target": 10, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 3 + ] + ], + "source": 26, + "target": 27, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 26, + "target": 9, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 3 + ] + ], + "source": 26, + "target": 27, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 26, + "target": 11, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 26, + "target": 10, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 27, + "target": 11, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 360 + ] + ], + "source": 27, + "target": 27, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 27, + "target": 10, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 360 + ] + ], + "source": 27, + "target": 27, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 27, + "target": 9, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 360 + ] + ], + "source": 27, + "target": 27, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 27, + "target": 11, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 27, + "target": 10, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 28, + "target": 29, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 28, + "target": 29, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 23 + ] + ], + "source": 29, + "target": 29, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 23 + ] + ], + "source": 29, + "target": 29, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 3 + ] + ], + "source": 30, + "target": 31, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 30, + "target": 11, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 3 + ] + ], + "source": 30, + "target": 31, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 30, + "target": 9, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 3 + ] + ], + "source": 30, + "target": 31, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 30, + "target": 11, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 360 + ] + ], + "source": 31, + "target": 31, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 31, + "target": 11, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 360 + ] + ], + "source": 31, + "target": 31, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 31, + "target": 9, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 360 + ] + ], + "source": 31, + "target": 31, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 31, + "target": 11, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 3 + ] + ], + "source": 33, + "target": 34, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 12 + ] + ], + "source": 34, + "target": 34, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 35, + "target": 9, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 3 + ] + ], + "source": 35, + "target": 36, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 35, + "target": 10, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 3 + ] + ], + "source": 35, + "target": 36, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 35, + "target": 9, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 3 + ] + ], + "source": 35, + "target": 36, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 35, + "target": 9, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 36, + "target": 9, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 360 + ] + ], + "source": 36, + "target": 36, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 36, + "target": 10, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 360 + ] + ], + "source": 36, + "target": 36, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 36, + "target": 9, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 360 + ] + ], + "source": 36, + "target": 36, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 36, + "target": 9, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 37, + "target": 13, + "timeDistribution": [] + } + ], + "nodes": [ + { + "file": "???", + "name": "???", + "object": "libc.so.6", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "analyze_fractal_tree", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "uint_macros.rs", + "name": "index", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "cmp.rs", + "name": "max", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "analyze_fractal_tree'2", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "build_fractal", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "build_fractal'2", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "clgctl.c", + "name": "clg_start", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "collect_leaves", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "cmp.rs", + "name": "spec_next", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "cmp.rs", + "name": "next", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "cmp.rs", + "name": "lt", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "collect_leaves'2", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "complex_fractal_benchmark", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "new", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "int_macros.rs", + "name": "wrapping_add", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "int_macros.rs", + "name": "rem_euclid", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "compute_child_value", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "uint_macros.rs", + "name": "wrapping_mul", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "compute_complexity_score", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "compute_tree_hash", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "compute_tree_hash'2", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "compute_variance", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "mut_ptr.rs", + "name": "into_iter", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "macros.rs", + "name": "next", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "non_null.rs", + "name": "eq", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "count_nodes", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "count_nodes'2", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "fibonacci_memo", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "fibonacci_memo'2", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "max_path_sum", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "max_path_sum'2", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "pool_alloc", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "recursive_path_score", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "recursive_path_score'2", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "recursive_sum", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "recursive_sum'2", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "run_measured", + "object": "fractal_rs", + "timeDistribution": [] + } + ], + "processes": { + "0": {} + }, + "roots": [ + 7 + ], + "threads": [ + [ + { + "pid": 0, + "tid": 0 + }, + {} + ] + ], + "version": 3 +} diff --git a/callgrind-utils/tests/snapshots/rust_callgraph__fractal_rs_folded.snap b/callgrind-utils/tests/snapshots/rust_callgraph__fractal_rs_folded.snap new file mode 100644 index 000000000..42f92d70d --- /dev/null +++ b/callgrind-utils/tests/snapshots/rust_callgraph__fractal_rs_folded.snap @@ -0,0 +1,218 @@ +--- +source: tests/rust_callgraph.rs +expression: "graph.to_folded_without_costs().join(\"\\n\")" +--- +clg_start +clg_start;run_measured +clg_start;run_measured;complex_fractal_benchmark +clg_start;run_measured;complex_fractal_benchmark;??? +clg_start;run_measured;complex_fractal_benchmark;build_fractal +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;pool_alloc +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_child_value +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;build_fractal'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_child_value +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;build_fractal'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_child_value +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;build_fractal'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;pool_alloc +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_child_value +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;build_fractal'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_child_value +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;build_fractal'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_child_value +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;build_fractal'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;pool_alloc +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_child_value +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;build_fractal'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_child_value +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;build_fractal'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_child_value +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;build_fractal'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash +clg_start;run_measured;complex_fractal_benchmark;build_fractal;compute_tree_hash +clg_start;run_measured;complex_fractal_benchmark;build_fractal;compute_tree_hash;wrapping_mul +clg_start;run_measured;complex_fractal_benchmark;build_fractal;compute_tree_hash;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;compute_tree_hash;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;compute_tree_hash;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;recursive_sum +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;recursive_sum;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;recursive_sum;recursive_sum'2;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;recursive_sum;recursive_sum'2;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;recursive_sum;recursive_sum'2;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;recursive_sum;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;recursive_sum;recursive_sum'2;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;recursive_sum;recursive_sum'2;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;recursive_sum;recursive_sum'2;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;recursive_sum;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;recursive_sum;recursive_sum'2;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;recursive_sum;recursive_sum'2;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;recursive_sum;recursive_sum'2;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;count_nodes +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;count_nodes;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;count_nodes;count_nodes'2;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;count_nodes;count_nodes'2;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;count_nodes;count_nodes'2;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;count_nodes;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;count_nodes;count_nodes'2;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;count_nodes;count_nodes'2;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;count_nodes;count_nodes'2;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;count_nodes;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;count_nodes;count_nodes'2;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;count_nodes;count_nodes'2;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;count_nodes;count_nodes'2;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;max_path_sum +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;max_path_sum;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;max_path_sum;max_path_sum'2;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;max_path_sum;max_path_sum'2;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;max_path_sum;max_path_sum'2;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;max_path_sum;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;max_path_sum;max_path_sum'2;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;max_path_sum;max_path_sum'2;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;max_path_sum;max_path_sum'2;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;max_path_sum;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;max_path_sum;max_path_sum'2;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;max_path_sum;max_path_sum'2;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;max_path_sum;max_path_sum'2;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;??? +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;collect_leaves +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;collect_leaves;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;collect_leaves;collect_leaves'2;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;collect_leaves;collect_leaves'2;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;collect_leaves;collect_leaves'2;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;collect_leaves;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;collect_leaves;collect_leaves'2;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;collect_leaves;collect_leaves'2;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;collect_leaves;collect_leaves'2;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;collect_leaves;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;collect_leaves;collect_leaves'2;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;collect_leaves;collect_leaves'2;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;collect_leaves;collect_leaves'2;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;compute_variance +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;recursive_sum +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;recursive_sum;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;recursive_sum;recursive_sum'2;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;recursive_sum;recursive_sum'2;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;recursive_sum;recursive_sum'2;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;recursive_sum;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;recursive_sum;recursive_sum'2;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;recursive_sum;recursive_sum'2;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;recursive_sum;recursive_sum'2;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;recursive_sum;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;recursive_sum;recursive_sum'2;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;recursive_sum;recursive_sum'2;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;recursive_sum;recursive_sum'2;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;count_nodes +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;count_nodes;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;count_nodes;count_nodes'2;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;count_nodes;count_nodes'2;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;count_nodes;count_nodes'2;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;count_nodes;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;count_nodes;count_nodes'2;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;count_nodes;count_nodes'2;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;count_nodes;count_nodes'2;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;count_nodes;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;count_nodes;count_nodes'2;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;count_nodes;count_nodes'2;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;count_nodes;count_nodes'2;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;max_path_sum +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;max_path_sum;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;max_path_sum;max_path_sum'2;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;max_path_sum;max_path_sum'2;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;max_path_sum;max_path_sum'2;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;max_path_sum;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;max_path_sum;max_path_sum'2;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;max_path_sum;max_path_sum'2;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;max_path_sum;max_path_sum'2;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;max_path_sum;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;max_path_sum;max_path_sum'2;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;max_path_sum;max_path_sum'2;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;max_path_sum;max_path_sum'2;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;??? +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;collect_leaves +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;collect_leaves;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;collect_leaves;collect_leaves'2;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;collect_leaves;collect_leaves'2;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;collect_leaves;collect_leaves'2;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;collect_leaves;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;collect_leaves;collect_leaves'2;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;collect_leaves;collect_leaves'2;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;collect_leaves;collect_leaves'2;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;collect_leaves;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;collect_leaves;collect_leaves'2;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;collect_leaves;collect_leaves'2;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;collect_leaves;collect_leaves'2;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;compute_variance +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;analyze_fractal_tree'2 +clg_start;run_measured;complex_fractal_benchmark;fibonacci_memo +clg_start;run_measured;complex_fractal_benchmark;fibonacci_memo;fibonacci_memo'2 +clg_start;run_measured;complex_fractal_benchmark;fibonacci_memo;fibonacci_memo'2;fibonacci_memo'2 +clg_start;run_measured;complex_fractal_benchmark;fibonacci_memo;fibonacci_memo'2;fibonacci_memo'2 +clg_start;run_measured;complex_fractal_benchmark;compute_tree_hash +clg_start;run_measured;complex_fractal_benchmark;compute_tree_hash;wrapping_mul +clg_start;run_measured;complex_fractal_benchmark;compute_tree_hash;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;compute_tree_hash;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;compute_tree_hash;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 diff --git a/callgrind-utils/tests/snapshots/rust_callgraph__fractal_rs_full__json.snap b/callgrind-utils/tests/snapshots/rust_callgraph__fractal_rs_full__json.snap new file mode 100644 index 000000000..10b0d2da8 --- /dev/null +++ b/callgrind-utils/tests/snapshots/rust_callgraph__fractal_rs_full__json.snap @@ -0,0 +1,2456 @@ +--- +source: tests/rust_callgraph.rs +expression: json +--- +{ + "edges": [ + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 1, + "target": 35, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 1, + "target": 26, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 1, + "target": 30, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 1, + "target": 0, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 1, + "target": 8, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 1, + "target": 2, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 1, + "target": 22, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 1, + "target": 4, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 1, + "target": 3, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 1, + "target": 19, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 2 + ] + ], + "source": 4, + "target": 35, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 2 + ] + ], + "source": 4, + "target": 26, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 2 + ] + ], + "source": 4, + "target": 30, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 2 + ] + ], + "source": 4, + "target": 0, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 2 + ] + ], + "source": 4, + "target": 8, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 4, + "target": 2, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 2 + ] + ], + "source": 4, + "target": 22, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 4, + "target": 4, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 4, + "target": 3, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 4, + "target": 19, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 4, + "target": 19, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 5, + "target": 32, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 5, + "target": 17, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 5, + "target": 6, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 5, + "target": 17, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 5, + "target": 6, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 5, + "target": 17, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 5, + "target": 6, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 5, + "target": 20, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 363 + ] + ], + "source": 6, + "target": 32, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 120 + ] + ], + "source": 6, + "target": 17, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 120 + ] + ], + "source": 6, + "target": 6, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 120 + ] + ], + "source": 6, + "target": 17, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 120 + ] + ], + "source": 6, + "target": 6, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 120 + ] + ], + "source": 6, + "target": 17, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 120 + ] + ], + "source": 6, + "target": 6, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 120 + ] + ], + "source": 6, + "target": 20, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 243 + ] + ], + "source": 6, + "target": 20, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 7, + "target": 37, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 3 + ] + ], + "source": 8, + "target": 12, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 8, + "target": 9, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 3 + ] + ], + "source": 8, + "target": 12, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 8, + "target": 10, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 3 + ] + ], + "source": 8, + "target": 12, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 8, + "target": 11, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 360 + ] + ], + "source": 12, + "target": 12, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 12, + "target": 9, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 360 + ] + ], + "source": 12, + "target": 12, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 12, + "target": 10, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 360 + ] + ], + "source": 12, + "target": 12, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 12, + "target": 11, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 13, + "target": 0, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 13, + "target": 14, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 13, + "target": 5, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 13, + "target": 1, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 13, + "target": 28, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 13, + "target": 20, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 13, + "target": 15, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 13, + "target": 16, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 17, + "target": 18, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 17, + "target": 18, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 3 + ] + ], + "source": 19, + "target": 33, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 20, + "target": 18, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 20, + "target": 15, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 20, + "target": 10, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 20, + "target": 18, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 122 + ] + ], + "source": 20, + "target": 21, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 20, + "target": 15, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 20, + "target": 11, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 20, + "target": 18, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 122 + ] + ], + "source": 20, + "target": 21, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 20, + "target": 15, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 20, + "target": 10, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 122 + ] + ], + "source": 20, + "target": 21, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 20, + "target": 9, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 21, + "target": 18, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 21, + "target": 15, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 21, + "target": 10, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 21, + "target": 18, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 546 + ] + ], + "source": 21, + "target": 21, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 21, + "target": 15, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 21, + "target": 11, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 21, + "target": 18, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 546 + ] + ], + "source": 21, + "target": 21, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 21, + "target": 15, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 21, + "target": 10, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 546 + ] + ], + "source": 21, + "target": 21, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 21, + "target": 9, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 22, + "target": 23, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 22, + "target": 24, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 22, + "target": 24, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 22, + "target": 24, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 22, + "target": 24, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 22, + "target": 25, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 22, + "target": 24, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 22, + "target": 25, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 22, + "target": 24, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 22, + "target": 24, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 22, + "target": 24, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 22, + "target": 24, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 22, + "target": 24, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 22, + "target": 24, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 22, + "target": 24, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 26, + "target": 11, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 3 + ] + ], + "source": 26, + "target": 27, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 26, + "target": 10, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 3 + ] + ], + "source": 26, + "target": 27, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 26, + "target": 9, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 3 + ] + ], + "source": 26, + "target": 27, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 26, + "target": 11, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 26, + "target": 10, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 27, + "target": 11, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 360 + ] + ], + "source": 27, + "target": 27, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 27, + "target": 10, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 360 + ] + ], + "source": 27, + "target": 27, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 27, + "target": 9, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 360 + ] + ], + "source": 27, + "target": 27, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 27, + "target": 11, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 27, + "target": 10, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 28, + "target": 29, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 28, + "target": 29, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 23 + ] + ], + "source": 29, + "target": 29, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 23 + ] + ], + "source": 29, + "target": 29, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 3 + ] + ], + "source": 30, + "target": 31, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 30, + "target": 11, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 3 + ] + ], + "source": 30, + "target": 31, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 30, + "target": 9, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 3 + ] + ], + "source": 30, + "target": 31, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 30, + "target": 11, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 360 + ] + ], + "source": 31, + "target": 31, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 31, + "target": 11, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 360 + ] + ], + "source": 31, + "target": 31, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 31, + "target": 9, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 360 + ] + ], + "source": 31, + "target": 31, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 31, + "target": 11, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 3 + ] + ], + "source": 33, + "target": 34, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 12 + ] + ], + "source": 34, + "target": 34, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 35, + "target": 9, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 3 + ] + ], + "source": 35, + "target": 36, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 35, + "target": 10, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 3 + ] + ], + "source": 35, + "target": 36, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 35, + "target": 9, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 3 + ] + ], + "source": 35, + "target": 36, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 35, + "target": 9, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 36, + "target": 9, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 360 + ] + ], + "source": 36, + "target": 36, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 36, + "target": 10, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 360 + ] + ], + "source": 36, + "target": 36, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 36, + "target": 9, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 360 + ] + ], + "source": 36, + "target": 36, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "inlined": true, + "source": 36, + "target": 9, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 37, + "target": 13, + "timeDistribution": [] + } + ], + "nodes": [ + { + "file": "???", + "name": "???", + "object": "libc.so.6", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "analyze_fractal_tree", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "uint_macros.rs", + "name": "index", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "cmp.rs", + "name": "max", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "analyze_fractal_tree'2", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "build_fractal", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "build_fractal'2", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "clgctl.c", + "name": "clg_start", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "collect_leaves", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "cmp.rs", + "name": "spec_next", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "cmp.rs", + "name": "next", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "cmp.rs", + "name": "lt", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "collect_leaves'2", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "complex_fractal_benchmark", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "new", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "int_macros.rs", + "name": "wrapping_add", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "int_macros.rs", + "name": "rem_euclid", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "compute_child_value", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "uint_macros.rs", + "name": "wrapping_mul", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "compute_complexity_score", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "compute_tree_hash", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "compute_tree_hash'2", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "compute_variance", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "mut_ptr.rs", + "name": "into_iter", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "macros.rs", + "name": "next", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "non_null.rs", + "name": "eq", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "count_nodes", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "count_nodes'2", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "fibonacci_memo", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "fibonacci_memo'2", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "max_path_sum", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "max_path_sum'2", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "pool_alloc", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "recursive_path_score", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "recursive_path_score'2", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "recursive_sum", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "recursive_sum'2", + "object": "fractal_rs", + "timeDistribution": [] + }, + { + "file": "fractal.rs", + "name": "run_measured", + "object": "fractal_rs", + "timeDistribution": [] + } + ], + "processes": { + "0": {} + }, + "roots": [ + 7 + ], + "threads": [ + [ + { + "pid": 0, + "tid": 0 + }, + {} + ] + ], + "version": 3 +} diff --git a/callgrind-utils/tests/snapshots/rust_callgraph__fractal_rs_full_folded.snap b/callgrind-utils/tests/snapshots/rust_callgraph__fractal_rs_full_folded.snap new file mode 100644 index 000000000..2e7d87780 --- /dev/null +++ b/callgrind-utils/tests/snapshots/rust_callgraph__fractal_rs_full_folded.snap @@ -0,0 +1,218 @@ +--- +source: tests/rust_callgraph.rs +expression: "graph.to_folded_without_costs().join(\"\\n\")" +--- +clg_start +clg_start;run_measured +clg_start;run_measured;complex_fractal_benchmark +clg_start;run_measured;complex_fractal_benchmark;__memset_avx2_unaligned_erms +clg_start;run_measured;complex_fractal_benchmark;build_fractal +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;pool_alloc +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_child_value +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;build_fractal'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_child_value +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;build_fractal'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_child_value +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;build_fractal'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;pool_alloc +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_child_value +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;build_fractal'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_child_value +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;build_fractal'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_child_value +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;build_fractal'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;pool_alloc +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_child_value +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;build_fractal'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_child_value +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;build_fractal'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_child_value +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;build_fractal'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash +clg_start;run_measured;complex_fractal_benchmark;build_fractal;compute_tree_hash +clg_start;run_measured;complex_fractal_benchmark;build_fractal;compute_tree_hash;wrapping_mul +clg_start;run_measured;complex_fractal_benchmark;build_fractal;compute_tree_hash;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;compute_tree_hash;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;compute_tree_hash;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;build_fractal;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;recursive_sum +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;recursive_sum;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;recursive_sum;recursive_sum'2;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;recursive_sum;recursive_sum'2;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;recursive_sum;recursive_sum'2;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;recursive_sum;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;recursive_sum;recursive_sum'2;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;recursive_sum;recursive_sum'2;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;recursive_sum;recursive_sum'2;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;recursive_sum;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;recursive_sum;recursive_sum'2;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;recursive_sum;recursive_sum'2;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;recursive_sum;recursive_sum'2;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;count_nodes +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;count_nodes;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;count_nodes;count_nodes'2;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;count_nodes;count_nodes'2;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;count_nodes;count_nodes'2;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;count_nodes;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;count_nodes;count_nodes'2;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;count_nodes;count_nodes'2;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;count_nodes;count_nodes'2;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;count_nodes;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;count_nodes;count_nodes'2;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;count_nodes;count_nodes'2;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;count_nodes;count_nodes'2;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;max_path_sum +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;max_path_sum;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;max_path_sum;max_path_sum'2;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;max_path_sum;max_path_sum'2;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;max_path_sum;max_path_sum'2;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;max_path_sum;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;max_path_sum;max_path_sum'2;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;max_path_sum;max_path_sum'2;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;max_path_sum;max_path_sum'2;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;max_path_sum;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;max_path_sum;max_path_sum'2;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;max_path_sum;max_path_sum'2;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;max_path_sum;max_path_sum'2;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;__memset_avx2_unaligned_erms +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;collect_leaves +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;collect_leaves;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;collect_leaves;collect_leaves'2;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;collect_leaves;collect_leaves'2;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;collect_leaves;collect_leaves'2;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;collect_leaves;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;collect_leaves;collect_leaves'2;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;collect_leaves;collect_leaves'2;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;collect_leaves;collect_leaves'2;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;collect_leaves;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;collect_leaves;collect_leaves'2;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;collect_leaves;collect_leaves'2;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;collect_leaves;collect_leaves'2;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;compute_variance +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;recursive_sum +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;recursive_sum;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;recursive_sum;recursive_sum'2;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;recursive_sum;recursive_sum'2;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;recursive_sum;recursive_sum'2;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;recursive_sum;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;recursive_sum;recursive_sum'2;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;recursive_sum;recursive_sum'2;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;recursive_sum;recursive_sum'2;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;recursive_sum;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;recursive_sum;recursive_sum'2;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;recursive_sum;recursive_sum'2;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;recursive_sum;recursive_sum'2;recursive_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;count_nodes +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;count_nodes;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;count_nodes;count_nodes'2;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;count_nodes;count_nodes'2;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;count_nodes;count_nodes'2;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;count_nodes;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;count_nodes;count_nodes'2;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;count_nodes;count_nodes'2;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;count_nodes;count_nodes'2;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;count_nodes;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;count_nodes;count_nodes'2;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;count_nodes;count_nodes'2;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;count_nodes;count_nodes'2;count_nodes'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;max_path_sum +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;max_path_sum;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;max_path_sum;max_path_sum'2;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;max_path_sum;max_path_sum'2;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;max_path_sum;max_path_sum'2;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;max_path_sum;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;max_path_sum;max_path_sum'2;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;max_path_sum;max_path_sum'2;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;max_path_sum;max_path_sum'2;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;max_path_sum;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;max_path_sum;max_path_sum'2;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;max_path_sum;max_path_sum'2;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;max_path_sum;max_path_sum'2;max_path_sum'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;__memset_avx2_unaligned_erms +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;collect_leaves +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;collect_leaves;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;collect_leaves;collect_leaves'2;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;collect_leaves;collect_leaves'2;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;collect_leaves;collect_leaves'2;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;collect_leaves;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;collect_leaves;collect_leaves'2;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;collect_leaves;collect_leaves'2;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;collect_leaves;collect_leaves'2;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;collect_leaves;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;collect_leaves;collect_leaves'2;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;collect_leaves;collect_leaves'2;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;collect_leaves;collect_leaves'2;collect_leaves'2 +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;compute_variance +clg_start;run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;analyze_fractal_tree'2 +clg_start;run_measured;complex_fractal_benchmark;fibonacci_memo +clg_start;run_measured;complex_fractal_benchmark;fibonacci_memo;fibonacci_memo'2 +clg_start;run_measured;complex_fractal_benchmark;fibonacci_memo;fibonacci_memo'2;fibonacci_memo'2 +clg_start;run_measured;complex_fractal_benchmark;fibonacci_memo;fibonacci_memo'2;fibonacci_memo'2 +clg_start;run_measured;complex_fractal_benchmark;compute_tree_hash +clg_start;run_measured;complex_fractal_benchmark;compute_tree_hash;wrapping_mul +clg_start;run_measured;complex_fractal_benchmark;compute_tree_hash;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;compute_tree_hash;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;compute_tree_hash;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +clg_start;run_measured;complex_fractal_benchmark;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 diff --git a/callgrind-utils/tests/snapshots/snapshot__chain__json.snap b/callgrind-utils/tests/snapshots/snapshot__chain__json.snap new file mode 100644 index 000000000..391892eb2 --- /dev/null +++ b/callgrind-utils/tests/snapshots/snapshot__chain__json.snap @@ -0,0 +1,92 @@ +--- +source: tests/snapshot.rs +expression: json +--- +{ + "edges": [ + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 0, + "target": 1, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 1, + "target": 2, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 3, + "target": 0, + "timeDistribution": [] + } + ], + "nodes": [ + { + "file": "chain.c", + "name": "a", + "object": "chain", + "timeDistribution": [] + }, + { + "file": "chain.c", + "name": "b", + "object": "chain", + "timeDistribution": [] + }, + { + "file": "chain.c", + "name": "c", + "object": "chain", + "timeDistribution": [] + }, + { + "file": "chain.c", + "name": "main", + "object": "chain", + "timeDistribution": [] + } + ], + "processes": { + "0": {} + }, + "roots": [ + 3 + ], + "threads": [ + [ + { + "pid": 0, + "tid": 0 + }, + {} + ] + ], + "version": 3 +} diff --git a/callgrind-utils/tests/snapshots/snapshot__chain_folded.snap b/callgrind-utils/tests/snapshots/snapshot__chain_folded.snap new file mode 100644 index 000000000..d5f629c72 --- /dev/null +++ b/callgrind-utils/tests/snapshots/snapshot__chain_folded.snap @@ -0,0 +1,8 @@ +--- +source: tests/snapshot.rs +expression: "graph.to_folded_without_costs().join(\"\\n\")" +--- +main +main;a +main;a;b +main;a;b;c diff --git a/callgrind-utils/tests/snapshots/snapshot__chain_full__json.snap b/callgrind-utils/tests/snapshots/snapshot__chain_full__json.snap new file mode 100644 index 000000000..391892eb2 --- /dev/null +++ b/callgrind-utils/tests/snapshots/snapshot__chain_full__json.snap @@ -0,0 +1,92 @@ +--- +source: tests/snapshot.rs +expression: json +--- +{ + "edges": [ + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 0, + "target": 1, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 1, + "target": 2, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 3, + "target": 0, + "timeDistribution": [] + } + ], + "nodes": [ + { + "file": "chain.c", + "name": "a", + "object": "chain", + "timeDistribution": [] + }, + { + "file": "chain.c", + "name": "b", + "object": "chain", + "timeDistribution": [] + }, + { + "file": "chain.c", + "name": "c", + "object": "chain", + "timeDistribution": [] + }, + { + "file": "chain.c", + "name": "main", + "object": "chain", + "timeDistribution": [] + } + ], + "processes": { + "0": {} + }, + "roots": [ + 3 + ], + "threads": [ + [ + { + "pid": 0, + "tid": 0 + }, + {} + ] + ], + "version": 3 +} diff --git a/callgrind-utils/tests/snapshots/snapshot__chain_full_folded.snap b/callgrind-utils/tests/snapshots/snapshot__chain_full_folded.snap new file mode 100644 index 000000000..d5f629c72 --- /dev/null +++ b/callgrind-utils/tests/snapshots/snapshot__chain_full_folded.snap @@ -0,0 +1,8 @@ +--- +source: tests/snapshot.rs +expression: "graph.to_folded_without_costs().join(\"\\n\")" +--- +main +main;a +main;a;b +main;a;b;c diff --git a/callgrind-utils/tests/snapshots/snapshot__diamond__json.snap b/callgrind-utils/tests/snapshots/snapshot__diamond__json.snap new file mode 100644 index 000000000..96fb716b2 --- /dev/null +++ b/callgrind-utils/tests/snapshots/snapshot__diamond__json.snap @@ -0,0 +1,126 @@ +--- +source: tests/snapshot.rs +expression: json +--- +{ + "edges": [ + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 1, + "target": 0, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 2, + "target": 4, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 3, + "target": 0, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 4, + "target": 1, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 4, + "target": 3, + "timeDistribution": [] + } + ], + "nodes": [ + { + "file": "diamond.c", + "name": "bottom", + "object": "diamond", + "timeDistribution": [] + }, + { + "file": "diamond.c", + "name": "left", + "object": "diamond", + "timeDistribution": [] + }, + { + "file": "diamond.c", + "name": "main", + "object": "diamond", + "timeDistribution": [] + }, + { + "file": "diamond.c", + "name": "right", + "object": "diamond", + "timeDistribution": [] + }, + { + "file": "diamond.c", + "name": "top", + "object": "diamond", + "timeDistribution": [] + } + ], + "processes": { + "0": {} + }, + "roots": [ + 2 + ], + "threads": [ + [ + { + "pid": 0, + "tid": 0 + }, + {} + ] + ], + "version": 3 +} diff --git a/callgrind-utils/tests/snapshots/snapshot__diamond_folded.snap b/callgrind-utils/tests/snapshots/snapshot__diamond_folded.snap new file mode 100644 index 000000000..bf4cf401b --- /dev/null +++ b/callgrind-utils/tests/snapshots/snapshot__diamond_folded.snap @@ -0,0 +1,10 @@ +--- +source: tests/snapshot.rs +expression: "graph.to_folded_without_costs().join(\"\\n\")" +--- +main +main;top +main;top;left +main;top;left;bottom +main;top;right +main;top;right;bottom diff --git a/callgrind-utils/tests/snapshots/snapshot__diamond_full__json.snap b/callgrind-utils/tests/snapshots/snapshot__diamond_full__json.snap new file mode 100644 index 000000000..96fb716b2 --- /dev/null +++ b/callgrind-utils/tests/snapshots/snapshot__diamond_full__json.snap @@ -0,0 +1,126 @@ +--- +source: tests/snapshot.rs +expression: json +--- +{ + "edges": [ + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 1, + "target": 0, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 2, + "target": 4, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 3, + "target": 0, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 4, + "target": 1, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 4, + "target": 3, + "timeDistribution": [] + } + ], + "nodes": [ + { + "file": "diamond.c", + "name": "bottom", + "object": "diamond", + "timeDistribution": [] + }, + { + "file": "diamond.c", + "name": "left", + "object": "diamond", + "timeDistribution": [] + }, + { + "file": "diamond.c", + "name": "main", + "object": "diamond", + "timeDistribution": [] + }, + { + "file": "diamond.c", + "name": "right", + "object": "diamond", + "timeDistribution": [] + }, + { + "file": "diamond.c", + "name": "top", + "object": "diamond", + "timeDistribution": [] + } + ], + "processes": { + "0": {} + }, + "roots": [ + 2 + ], + "threads": [ + [ + { + "pid": 0, + "tid": 0 + }, + {} + ] + ], + "version": 3 +} diff --git a/callgrind-utils/tests/snapshots/snapshot__diamond_full_folded.snap b/callgrind-utils/tests/snapshots/snapshot__diamond_full_folded.snap new file mode 100644 index 000000000..bf4cf401b --- /dev/null +++ b/callgrind-utils/tests/snapshots/snapshot__diamond_full_folded.snap @@ -0,0 +1,10 @@ +--- +source: tests/snapshot.rs +expression: "graph.to_folded_without_costs().join(\"\\n\")" +--- +main +main;top +main;top;left +main;top;left;bottom +main;top;right +main;top;right;bottom diff --git a/callgrind-utils/tests/snapshots/snapshot__fractal__json.snap b/callgrind-utils/tests/snapshots/snapshot__fractal__json.snap new file mode 100644 index 000000000..d872ede9d --- /dev/null +++ b/callgrind-utils/tests/snapshots/snapshot__fractal__json.snap @@ -0,0 +1,800 @@ +--- +source: tests/snapshot.rs +expression: json +--- +{ + "edges": [ + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 0, + "target": 21, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 0, + "target": 12, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 0, + "target": 16, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 0, + "target": 4, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 0, + "target": 11, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 0, + "target": 1, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 0, + "target": 8, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 2 + ] + ], + "source": 1, + "target": 21, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 2 + ] + ], + "source": 1, + "target": 12, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 2 + ] + ], + "source": 1, + "target": 16, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 2 + ] + ], + "source": 1, + "target": 4, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 2 + ] + ], + "source": 1, + "target": 11, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 1, + "target": 1, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 1, + "target": 8, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 1, + "target": 8, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 2, + "target": 18, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 3 + ] + ], + "source": 2, + "target": 7, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 3 + ] + ], + "source": 2, + "target": 3, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 2, + "target": 9, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 363 + ] + ], + "source": 3, + "target": 18, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 360 + ] + ], + "source": 3, + "target": 7, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 360 + ] + ], + "source": 3, + "target": 3, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 363 + ] + ], + "source": 3, + "target": 9, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 9 + ] + ], + "source": 4, + "target": 5, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1080 + ] + ], + "source": 5, + "target": 5, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 6, + "target": 2, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 6, + "target": 0, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 6, + "target": 14, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 6, + "target": 9, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 3 + ] + ], + "source": 8, + "target": 19, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 366 + ] + ], + "source": 9, + "target": 10, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1638 + ] + ], + "source": 10, + "target": 10, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 9 + ] + ], + "source": 12, + "target": 13, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1080 + ] + ], + "source": 13, + "target": 13, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 14, + "target": 15, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 14, + "target": 15, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 23 + ] + ], + "source": 15, + "target": 15, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 23 + ] + ], + "source": 15, + "target": 15, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 9 + ] + ], + "source": 16, + "target": 17, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1080 + ] + ], + "source": 17, + "target": 17, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 3 + ] + ], + "source": 19, + "target": 20, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 12 + ] + ], + "source": 20, + "target": 20, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 9 + ] + ], + "source": 21, + "target": 22, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1080 + ] + ], + "source": 22, + "target": 22, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 23, + "target": 6, + "timeDistribution": [] + } + ], + "nodes": [ + { + "file": "fractal.c", + "name": "analyze_fractal_tree", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "analyze_fractal_tree'2", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "build_fractal", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "build_fractal'2", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "collect_leaves", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "collect_leaves'2", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "complex_fractal_benchmark", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "compute_child_value", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "compute_complexity_score", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "compute_tree_hash", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "compute_tree_hash'2", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "compute_variance", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "count_nodes", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "count_nodes'2", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "fibonacci_memo", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "fibonacci_memo'2", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "max_path_sum", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "max_path_sum'2", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "pool_alloc", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "recursive_path_score", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "recursive_path_score'2", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "recursive_sum", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "recursive_sum'2", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "run_measured", + "object": "fractal", + "timeDistribution": [] + } + ], + "processes": { + "0": {} + }, + "roots": [ + 23 + ], + "threads": [ + [ + { + "pid": 0, + "tid": 0 + }, + {} + ] + ], + "version": 3 +} diff --git a/callgrind-utils/tests/snapshots/snapshot__fractal_folded.snap b/callgrind-utils/tests/snapshots/snapshot__fractal_folded.snap new file mode 100644 index 000000000..6d169ec4e --- /dev/null +++ b/callgrind-utils/tests/snapshots/snapshot__fractal_folded.snap @@ -0,0 +1,55 @@ +--- +source: tests/snapshot.rs +expression: "graph.to_folded_without_costs().join(\"\\n\")" +--- +run_measured +run_measured;complex_fractal_benchmark +run_measured;complex_fractal_benchmark;build_fractal +run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2 +run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;pool_alloc +run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_child_value +run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;build_fractal'2 +run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash +run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2 +run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +run_measured;complex_fractal_benchmark;build_fractal;compute_tree_hash +run_measured;complex_fractal_benchmark;build_fractal;compute_tree_hash;compute_tree_hash'2 +run_measured;complex_fractal_benchmark;build_fractal;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree +run_measured;complex_fractal_benchmark;analyze_fractal_tree;recursive_sum +run_measured;complex_fractal_benchmark;analyze_fractal_tree;recursive_sum;recursive_sum'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree;recursive_sum;recursive_sum'2;recursive_sum'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree;count_nodes +run_measured;complex_fractal_benchmark;analyze_fractal_tree;count_nodes;count_nodes'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree;count_nodes;count_nodes'2;count_nodes'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree;max_path_sum +run_measured;complex_fractal_benchmark;analyze_fractal_tree;max_path_sum;max_path_sum'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree;max_path_sum;max_path_sum'2;max_path_sum'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree;collect_leaves +run_measured;complex_fractal_benchmark;analyze_fractal_tree;collect_leaves;collect_leaves'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree;collect_leaves;collect_leaves'2;collect_leaves'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree;compute_variance +run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;recursive_sum +run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;recursive_sum;recursive_sum'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;recursive_sum;recursive_sum'2;recursive_sum'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;count_nodes +run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;count_nodes;count_nodes'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;count_nodes;count_nodes'2;count_nodes'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;max_path_sum +run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;max_path_sum;max_path_sum'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;max_path_sum;max_path_sum'2;max_path_sum'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;collect_leaves +run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;collect_leaves;collect_leaves'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;collect_leaves;collect_leaves'2;collect_leaves'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;compute_variance +run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;analyze_fractal_tree'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree;compute_complexity_score +run_measured;complex_fractal_benchmark;analyze_fractal_tree;compute_complexity_score;recursive_path_score +run_measured;complex_fractal_benchmark;fibonacci_memo +run_measured;complex_fractal_benchmark;fibonacci_memo;fibonacci_memo'2 +run_measured;complex_fractal_benchmark;fibonacci_memo;fibonacci_memo'2;fibonacci_memo'2 +run_measured;complex_fractal_benchmark;fibonacci_memo;fibonacci_memo'2;fibonacci_memo'2 +run_measured;complex_fractal_benchmark;compute_tree_hash +run_measured;complex_fractal_benchmark;compute_tree_hash;compute_tree_hash'2 +run_measured;complex_fractal_benchmark;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 diff --git a/callgrind-utils/tests/snapshots/snapshot__fractal_full__json.snap b/callgrind-utils/tests/snapshots/snapshot__fractal_full__json.snap new file mode 100644 index 000000000..d872ede9d --- /dev/null +++ b/callgrind-utils/tests/snapshots/snapshot__fractal_full__json.snap @@ -0,0 +1,800 @@ +--- +source: tests/snapshot.rs +expression: json +--- +{ + "edges": [ + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 0, + "target": 21, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 0, + "target": 12, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 0, + "target": 16, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 0, + "target": 4, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 0, + "target": 11, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 0, + "target": 1, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 0, + "target": 8, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 2 + ] + ], + "source": 1, + "target": 21, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 2 + ] + ], + "source": 1, + "target": 12, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 2 + ] + ], + "source": 1, + "target": 16, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 2 + ] + ], + "source": 1, + "target": 4, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 2 + ] + ], + "source": 1, + "target": 11, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 1, + "target": 1, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 1, + "target": 8, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 1, + "target": 8, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 2, + "target": 18, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 3 + ] + ], + "source": 2, + "target": 7, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 3 + ] + ], + "source": 2, + "target": 3, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 2, + "target": 9, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 363 + ] + ], + "source": 3, + "target": 18, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 360 + ] + ], + "source": 3, + "target": 7, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 360 + ] + ], + "source": 3, + "target": 3, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 363 + ] + ], + "source": 3, + "target": 9, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 9 + ] + ], + "source": 4, + "target": 5, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1080 + ] + ], + "source": 5, + "target": 5, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 6, + "target": 2, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 6, + "target": 0, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 6, + "target": 14, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 6, + "target": 9, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 3 + ] + ], + "source": 8, + "target": 19, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 366 + ] + ], + "source": 9, + "target": 10, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1638 + ] + ], + "source": 10, + "target": 10, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 9 + ] + ], + "source": 12, + "target": 13, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1080 + ] + ], + "source": 13, + "target": 13, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 14, + "target": 15, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 14, + "target": 15, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 23 + ] + ], + "source": 15, + "target": 15, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 23 + ] + ], + "source": 15, + "target": 15, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 9 + ] + ], + "source": 16, + "target": 17, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1080 + ] + ], + "source": 17, + "target": 17, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 3 + ] + ], + "source": 19, + "target": 20, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 12 + ] + ], + "source": 20, + "target": 20, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 9 + ] + ], + "source": 21, + "target": 22, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1080 + ] + ], + "source": 22, + "target": 22, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 23, + "target": 6, + "timeDistribution": [] + } + ], + "nodes": [ + { + "file": "fractal.c", + "name": "analyze_fractal_tree", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "analyze_fractal_tree'2", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "build_fractal", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "build_fractal'2", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "collect_leaves", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "collect_leaves'2", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "complex_fractal_benchmark", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "compute_child_value", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "compute_complexity_score", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "compute_tree_hash", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "compute_tree_hash'2", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "compute_variance", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "count_nodes", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "count_nodes'2", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "fibonacci_memo", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "fibonacci_memo'2", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "max_path_sum", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "max_path_sum'2", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "pool_alloc", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "recursive_path_score", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "recursive_path_score'2", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "recursive_sum", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "recursive_sum'2", + "object": "fractal", + "timeDistribution": [] + }, + { + "file": "fractal.c", + "name": "run_measured", + "object": "fractal", + "timeDistribution": [] + } + ], + "processes": { + "0": {} + }, + "roots": [ + 23 + ], + "threads": [ + [ + { + "pid": 0, + "tid": 0 + }, + {} + ] + ], + "version": 3 +} diff --git a/callgrind-utils/tests/snapshots/snapshot__fractal_full_folded.snap b/callgrind-utils/tests/snapshots/snapshot__fractal_full_folded.snap new file mode 100644 index 000000000..6d169ec4e --- /dev/null +++ b/callgrind-utils/tests/snapshots/snapshot__fractal_full_folded.snap @@ -0,0 +1,55 @@ +--- +source: tests/snapshot.rs +expression: "graph.to_folded_without_costs().join(\"\\n\")" +--- +run_measured +run_measured;complex_fractal_benchmark +run_measured;complex_fractal_benchmark;build_fractal +run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2 +run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;pool_alloc +run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_child_value +run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;build_fractal'2 +run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash +run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2 +run_measured;complex_fractal_benchmark;build_fractal;build_fractal'2;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +run_measured;complex_fractal_benchmark;build_fractal;compute_tree_hash +run_measured;complex_fractal_benchmark;build_fractal;compute_tree_hash;compute_tree_hash'2 +run_measured;complex_fractal_benchmark;build_fractal;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree +run_measured;complex_fractal_benchmark;analyze_fractal_tree;recursive_sum +run_measured;complex_fractal_benchmark;analyze_fractal_tree;recursive_sum;recursive_sum'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree;recursive_sum;recursive_sum'2;recursive_sum'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree;count_nodes +run_measured;complex_fractal_benchmark;analyze_fractal_tree;count_nodes;count_nodes'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree;count_nodes;count_nodes'2;count_nodes'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree;max_path_sum +run_measured;complex_fractal_benchmark;analyze_fractal_tree;max_path_sum;max_path_sum'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree;max_path_sum;max_path_sum'2;max_path_sum'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree;collect_leaves +run_measured;complex_fractal_benchmark;analyze_fractal_tree;collect_leaves;collect_leaves'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree;collect_leaves;collect_leaves'2;collect_leaves'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree;compute_variance +run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;recursive_sum +run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;recursive_sum;recursive_sum'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;recursive_sum;recursive_sum'2;recursive_sum'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;count_nodes +run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;count_nodes;count_nodes'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;count_nodes;count_nodes'2;count_nodes'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;max_path_sum +run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;max_path_sum;max_path_sum'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;max_path_sum;max_path_sum'2;max_path_sum'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;collect_leaves +run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;collect_leaves;collect_leaves'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;collect_leaves;collect_leaves'2;collect_leaves'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;compute_variance +run_measured;complex_fractal_benchmark;analyze_fractal_tree;analyze_fractal_tree'2;analyze_fractal_tree'2 +run_measured;complex_fractal_benchmark;analyze_fractal_tree;compute_complexity_score +run_measured;complex_fractal_benchmark;analyze_fractal_tree;compute_complexity_score;recursive_path_score +run_measured;complex_fractal_benchmark;fibonacci_memo +run_measured;complex_fractal_benchmark;fibonacci_memo;fibonacci_memo'2 +run_measured;complex_fractal_benchmark;fibonacci_memo;fibonacci_memo'2;fibonacci_memo'2 +run_measured;complex_fractal_benchmark;fibonacci_memo;fibonacci_memo'2;fibonacci_memo'2 +run_measured;complex_fractal_benchmark;compute_tree_hash +run_measured;complex_fractal_benchmark;compute_tree_hash;compute_tree_hash'2 +run_measured;complex_fractal_benchmark;compute_tree_hash;compute_tree_hash'2;compute_tree_hash'2 diff --git a/callgrind-utils/tests/snapshots/snapshot__mutual__json.snap b/callgrind-utils/tests/snapshots/snapshot__mutual__json.snap new file mode 100644 index 000000000..d03921bf5 --- /dev/null +++ b/callgrind-utils/tests/snapshots/snapshot__mutual__json.snap @@ -0,0 +1,126 @@ +--- +source: tests/snapshot.rs +expression: json +--- +{ + "edges": [ + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 0, + "target": 2, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 2 + ] + ], + "source": 1, + "target": 3, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 2, + "target": 1, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 2 + ] + ], + "source": 3, + "target": 1, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 4, + "target": 0, + "timeDistribution": [] + } + ], + "nodes": [ + { + "file": "mutual.c", + "name": "is_even", + "object": "mutual", + "timeDistribution": [] + }, + { + "file": "mutual.c", + "name": "is_even'2", + "object": "mutual", + "timeDistribution": [] + }, + { + "file": "mutual.c", + "name": "is_odd", + "object": "mutual", + "timeDistribution": [] + }, + { + "file": "mutual.c", + "name": "is_odd'2", + "object": "mutual", + "timeDistribution": [] + }, + { + "file": "mutual.c", + "name": "main", + "object": "mutual", + "timeDistribution": [] + } + ], + "processes": { + "0": {} + }, + "roots": [ + 4 + ], + "threads": [ + [ + { + "pid": 0, + "tid": 0 + }, + {} + ] + ], + "version": 3 +} diff --git a/callgrind-utils/tests/snapshots/snapshot__mutual_folded.snap b/callgrind-utils/tests/snapshots/snapshot__mutual_folded.snap new file mode 100644 index 000000000..4be66e3ac --- /dev/null +++ b/callgrind-utils/tests/snapshots/snapshot__mutual_folded.snap @@ -0,0 +1,10 @@ +--- +source: tests/snapshot.rs +expression: "graph.to_folded_without_costs().join(\"\\n\")" +--- +main +main;is_even +main;is_even;is_odd +main;is_even;is_odd;is_even'2 +main;is_even;is_odd;is_even'2;is_odd'2 +main;is_even;is_odd;is_even'2;is_odd'2;is_even'2 diff --git a/callgrind-utils/tests/snapshots/snapshot__mutual_full__json.snap b/callgrind-utils/tests/snapshots/snapshot__mutual_full__json.snap new file mode 100644 index 000000000..d03921bf5 --- /dev/null +++ b/callgrind-utils/tests/snapshots/snapshot__mutual_full__json.snap @@ -0,0 +1,126 @@ +--- +source: tests/snapshot.rs +expression: json +--- +{ + "edges": [ + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 0, + "target": 2, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 2 + ] + ], + "source": 1, + "target": 3, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 2, + "target": 1, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 2 + ] + ], + "source": 3, + "target": 1, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 4, + "target": 0, + "timeDistribution": [] + } + ], + "nodes": [ + { + "file": "mutual.c", + "name": "is_even", + "object": "mutual", + "timeDistribution": [] + }, + { + "file": "mutual.c", + "name": "is_even'2", + "object": "mutual", + "timeDistribution": [] + }, + { + "file": "mutual.c", + "name": "is_odd", + "object": "mutual", + "timeDistribution": [] + }, + { + "file": "mutual.c", + "name": "is_odd'2", + "object": "mutual", + "timeDistribution": [] + }, + { + "file": "mutual.c", + "name": "main", + "object": "mutual", + "timeDistribution": [] + } + ], + "processes": { + "0": {} + }, + "roots": [ + 4 + ], + "threads": [ + [ + { + "pid": 0, + "tid": 0 + }, + {} + ] + ], + "version": 3 +} diff --git a/callgrind-utils/tests/snapshots/snapshot__mutual_full_folded.snap b/callgrind-utils/tests/snapshots/snapshot__mutual_full_folded.snap new file mode 100644 index 000000000..4be66e3ac --- /dev/null +++ b/callgrind-utils/tests/snapshots/snapshot__mutual_full_folded.snap @@ -0,0 +1,10 @@ +--- +source: tests/snapshot.rs +expression: "graph.to_folded_without_costs().join(\"\\n\")" +--- +main +main;is_even +main;is_even;is_odd +main;is_even;is_odd;is_even'2 +main;is_even;is_odd;is_even'2;is_odd'2 +main;is_even;is_odd;is_even'2;is_odd'2;is_even'2 diff --git a/callgrind-utils/tests/snapshots/snapshot__recursion__json.snap b/callgrind-utils/tests/snapshots/snapshot__recursion__json.snap new file mode 100644 index 000000000..855e81eaa --- /dev/null +++ b/callgrind-utils/tests/snapshots/snapshot__recursion__json.snap @@ -0,0 +1,154 @@ +--- +source: tests/snapshot.rs +expression: json +--- +{ + "edges": [ + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 0, + "target": 1, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 0, + "target": 4, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 1, + "target": 2, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 1, + "target": 2, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 32 + ] + ], + "source": 2, + "target": 2, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 32 + ] + ], + "source": 2, + "target": 2, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 3, + "target": 0, + "timeDistribution": [] + } + ], + "nodes": [ + { + "file": "recursion.c", + "name": "compute", + "object": "recursion", + "timeDistribution": [] + }, + { + "file": "recursion.c", + "name": "fib", + "object": "recursion", + "timeDistribution": [] + }, + { + "file": "recursion.c", + "name": "fib'2", + "object": "recursion", + "timeDistribution": [] + }, + { + "file": "recursion.c", + "name": "main", + "object": "recursion", + "timeDistribution": [] + }, + { + "file": "recursion.c", + "name": "square", + "object": "recursion", + "timeDistribution": [] + } + ], + "processes": { + "0": {} + }, + "roots": [ + 3 + ], + "threads": [ + [ + { + "pid": 0, + "tid": 0 + }, + {} + ] + ], + "version": 3 +} diff --git a/callgrind-utils/tests/snapshots/snapshot__recursion_folded.snap b/callgrind-utils/tests/snapshots/snapshot__recursion_folded.snap new file mode 100644 index 000000000..79a289833 --- /dev/null +++ b/callgrind-utils/tests/snapshots/snapshot__recursion_folded.snap @@ -0,0 +1,14 @@ +--- +source: tests/snapshot.rs +expression: "graph.to_folded_without_costs().join(\"\\n\")" +--- +main +main;compute +main;compute;fib +main;compute;fib;fib'2 +main;compute;fib;fib'2;fib'2 +main;compute;fib;fib'2;fib'2 +main;compute;fib;fib'2 +main;compute;fib;fib'2;fib'2 +main;compute;fib;fib'2;fib'2 +main;compute;square diff --git a/callgrind-utils/tests/snapshots/snapshot__recursion_full__json.snap b/callgrind-utils/tests/snapshots/snapshot__recursion_full__json.snap new file mode 100644 index 000000000..855e81eaa --- /dev/null +++ b/callgrind-utils/tests/snapshots/snapshot__recursion_full__json.snap @@ -0,0 +1,154 @@ +--- +source: tests/snapshot.rs +expression: json +--- +{ + "edges": [ + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 0, + "target": 1, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 0, + "target": 4, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 1, + "target": 2, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 1, + "target": 2, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 32 + ] + ], + "source": 2, + "target": 2, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 32 + ] + ], + "source": 2, + "target": 2, + "timeDistribution": [] + }, + { + "counts": [ + [ + { + "pid": 0, + "tid": 0 + }, + 1 + ] + ], + "source": 3, + "target": 0, + "timeDistribution": [] + } + ], + "nodes": [ + { + "file": "recursion.c", + "name": "compute", + "object": "recursion", + "timeDistribution": [] + }, + { + "file": "recursion.c", + "name": "fib", + "object": "recursion", + "timeDistribution": [] + }, + { + "file": "recursion.c", + "name": "fib'2", + "object": "recursion", + "timeDistribution": [] + }, + { + "file": "recursion.c", + "name": "main", + "object": "recursion", + "timeDistribution": [] + }, + { + "file": "recursion.c", + "name": "square", + "object": "recursion", + "timeDistribution": [] + } + ], + "processes": { + "0": {} + }, + "roots": [ + 3 + ], + "threads": [ + [ + { + "pid": 0, + "tid": 0 + }, + {} + ] + ], + "version": 3 +} diff --git a/callgrind-utils/tests/snapshots/snapshot__recursion_full_folded.snap b/callgrind-utils/tests/snapshots/snapshot__recursion_full_folded.snap new file mode 100644 index 000000000..79a289833 --- /dev/null +++ b/callgrind-utils/tests/snapshots/snapshot__recursion_full_folded.snap @@ -0,0 +1,14 @@ +--- +source: tests/snapshot.rs +expression: "graph.to_folded_without_costs().join(\"\\n\")" +--- +main +main;compute +main;compute;fib +main;compute;fib;fib'2 +main;compute;fib;fib'2;fib'2 +main;compute;fib;fib'2;fib'2 +main;compute;fib;fib'2 +main;compute;fib;fib'2;fib'2 +main;compute;fib;fib'2;fib'2 +main;compute;square diff --git a/callgrind/bbcc.c b/callgrind/bbcc.c index 9b08923d3..85f43ff96 100644 --- a/callgrind/bbcc.c +++ b/callgrind/bbcc.c @@ -554,6 +554,24 @@ static void handleUnderflow(BB* bb) } +static Bool return_matches_call_entry(call_entry* ce, BB* bb) +{ + if (ce->ret_addr == bb_addr(bb)) return True; + +#if defined(VGA_arm64) + /* AArch64 same-SP returns can land in a caller continuation BB whose + * start address is not exactly the saved architectural LR. The function + * identity still tells us this is a return to the recorded caller, not a + * fresh call from the callee back into its parent. */ + if (ce->jcc && ce->jcc->from && ce->jcc->from->cxt && + (ce->jcc->from->cxt->fn[0] == CLG_(get_fn_node)(bb))) + return True; +#endif + + return False; +} + + /* * Helper function called at start of each instrumented BB to setup * pointer to costs for current thread/context/recursion level @@ -654,42 +672,110 @@ void CLG_(setup_bbcc)(BB* bb) csp = CLG_(current_call_stack).sp; - /* A return not matching the top call in our callstack is a jump */ + /* A return not matching the top call in our callstack is a jump. + * + * `popcount_on_return` is only the number of SP-equal frames that the + * unwinder may pop after all strictly-lower frames have been discarded. + * A strictly-lower top frame is already proof that a return left at least + * that frame, but it is NOT proof that the first SP-equal caller frame also + * returned. On AArch64/PPC a caller may restore SP to its own entry value + * before executing its continuation; popping that equal-SP caller early + * closes its call edge before its post-child self cost is seen. + */ if ( (jmpkind == jk_Return) && (csp >0)) { - Int csp_up = csp-1; + Int csp_up = csp-1; call_entry* top_ce = &(CLG_(current_call_stack).entry[csp_up]); + Bool real_return = True; - /* We have a real return if - * - the stack pointer (SP) left the current stack frame, or - * - SP has the same value as when reaching the current function - * and the address of this BB is the return address of last call - * (we even allow to leave multiple frames if the SP stays the - * same and we find a matching return address) - * The latter condition is needed because on PPC, SP can stay - * the same over CALL=b(c)l / RET=b(c)lr boundaries - */ - if (sp < top_ce->sp) popcount_on_return = 0; + if (sp < top_ce->sp) { + real_return = False; + popcount_on_return = 0; + } else if (top_ce->sp == sp) { - while(1) { - if (top_ce->ret_addr == bb_addr(bb)) break; - if (csp_up>0) { - csp_up--; - top_ce = &(CLG_(current_call_stack).entry[csp_up]); - if (top_ce->sp == sp) { - popcount_on_return++; - continue; - } + popcount_on_return = 1; + while (!return_matches_call_entry(top_ce, bb)) { + if (csp_up == 0) { + real_return = False; + popcount_on_return = 0; + break; + } + csp_up--; + top_ce = &(CLG_(current_call_stack).entry[csp_up]); + if (top_ce->sp == sp) { + popcount_on_return++; + continue; } + real_return = False; popcount_on_return = 0; break; } } - if (popcount_on_return == 0) { + else { + Int equal_pops = 0; + Bool found_equal_target = False; + + popcount_on_return = 0; + while (csp_up > 0) { + csp_up--; + top_ce = &(CLG_(current_call_stack).entry[csp_up]); + if (top_ce->sp != sp) continue; + + equal_pops++; + if (return_matches_call_entry(top_ce, bb)) { + found_equal_target = True; + break; + } + } + if (found_equal_target) + popcount_on_return = equal_pops; + } + if (!real_return) { jmpkind = jk_Jump; ret_without_call = True; } } + /* Some targets return with an ordinary branch rather than a RET-kind jump + * (for example an indirect branch to LR). If the branch target is a return + * address already recorded in the call stack, handle it as a return before + * the jump-to-call heuristic below can turn the caller continuation into a + * bogus call edge. As above, popcount_on_return only budgets SP-equal pops; + * strictly-lower frames are popped by unwind_call_stack() regardless. */ + if ((jmpkind != jk_Return) && (jmpkind != jk_Call) && (csp > 0)) { + Int csp_up = csp-1; + Int equal_pops = 0; + Bool found_return_target = False; + + while (1) { + call_entry* top_ce = &(CLG_(current_call_stack).entry[csp_up]); + + if (top_ce->sp < sp) { + if (return_matches_call_entry(top_ce, bb)) { + found_return_target = True; + break; + } + } + else if (top_ce->sp == sp) { + equal_pops++; + if (return_matches_call_entry(top_ce, bb)) { + found_return_target = True; + break; + } + } + else { + break; + } + + if (csp_up == 0) break; + csp_up--; + } + + if (found_return_target) { + jmpkind = jk_Return; + popcount_on_return = equal_pops; + } + } + /* Should this jump be converted to call or pop/call ? */ if (( jmpkind != jk_Return) && ( jmpkind != jk_Call) && last_bb) { @@ -789,7 +875,6 @@ void CLG_(setup_bbcc)(BB* bb) CLG_(pop_call_stack)(); } else { - CLG_ASSERT(popcount_on_return >0); CLG_(unwind_call_stack)(sp, popcount_on_return); } } diff --git a/callgrind/callstack.c b/callgrind/callstack.c index 8951639d7..6bca9a6c5 100644 --- a/callgrind/callstack.c +++ b/callgrind/callstack.c @@ -431,13 +431,28 @@ Int CLG_(unwind_call_stack)(Addr sp, Int minpops) while( (csp=CLG_(current_call_stack).sp) >0) { call_entry* top_ce = &(CLG_(current_call_stack).entry[csp-1]); - if ((top_ce->sp < sp) || - ((top_ce->sp == sp) && minpops>0)) { - + /* A frame whose entry SP is strictly below the new SP has been left by + * the return and is always unwound. On targets where the call + * instruction does not move SP (AArch64 bl/blr, PPC b(c)l), a callee's + * own *entry* frame records the caller's SP and therefore sits at SP + * *equal* to the return target; such frames sit beneath the SP-lower + * frames of any sub-calls the callee made. The minpops budget bounds + * how many of these SP-equal frames a single return may pop (computed by + * the ret_addr-matching logic in setup_bbcc). SP-lower pops must NOT + * consume that budget — otherwise sub-call frames exhaust it and the + * SP-equal entry frame is left stuck on the stack, keeping the callee's + * context active so the caller's continuation is mis-attributed to the + * callee (inverted edges) and the callee's never-decremented recursion + * depth fabricates spurious recursion clones. */ + if (top_ce->sp < sp) { + unwind_count++; + CLG_(pop_call_stack)(); + continue; + } + if ((top_ce->sp == sp) && minpops>0) { minpops--; unwind_count++; CLG_(pop_call_stack)(); - csp=CLG_(current_call_stack).sp; continue; } break; diff --git a/coregrind/m_debuginfo/readelf.c b/coregrind/m_debuginfo/readelf.c index 58ffc9b53..e59d06a0f 100644 --- a/coregrind/m_debuginfo/readelf.c +++ b/coregrind/m_debuginfo/readelf.c @@ -1500,6 +1500,37 @@ DiImage* find_debug_file_debuginfod( const HChar* objpath, } #endif +/* Try one directory as a root for the standard .build-id/xx/yyyy.debug + layout. On success, returns the opened image and sets *debugpath_out + to a freshly allocated path (which the caller owns); on failure, + returns NULL and leaves *debugpath_out untouched. */ +static +DiImage* try_buildid_dir( const HChar* dir, SizeT dirlen, + const HChar* buildid, Bool rel_ok, + HChar** debugpath_out ) +{ + DiImage* dimg; + HChar* debugpath; + + if (dirlen == 0) + return NULL; + + debugpath = ML_(dinfo_zalloc)("di.tbid.1", + dirlen + VG_(strlen)(buildid) + 19); + VG_(memcpy)(debugpath, dir, dirlen); + VG_(sprintf)(debugpath + dirlen, "/.build-id/%c%c/%s.debug", + buildid[0], buildid[1], buildid + 2); + + dimg = open_debug_file(debugpath, buildid, 0, rel_ok, NULL); + if (dimg == NULL) { + ML_(dinfo_free)(debugpath); + return NULL; + } + + *debugpath_out = debugpath; + return dimg; +} + /* Try to find a separate debug file for a given object file. If found, return its DiImage, which should be freed by the caller. If |buildid| is non-NULL, then a debug object matching it is @@ -1519,16 +1550,42 @@ DiImage* find_debug_file( struct _DebugInfo* di, HChar* debugpath = NULL; /* where we found it */ if (buildid != NULL) { - debugpath = ML_(dinfo_zalloc)("di.fdf.1", - VG_(strlen)(buildid) + 33); + /* Nix packages ship separate debug outputs under their own store + paths, never under /usr/lib/debug (which doesn't exist on + NixOS). NIX_DEBUG_INFO_DIRS is the established convention (also + honoured by gdb/lldb via nixpkgs wrappers) for a colon-separated + list of trees that mirror the standard .build-id/xx/yyyy.debug + layout; try each, then --extra-debuginfo-path, before falling + back to the FHS path. */ + const HChar* nix_dirs = VG_(getenv)("NIX_DEBUG_INFO_DIRS"); + const HChar* p = nix_dirs; + + while (dimg == NULL && p != NULL && *p != 0) { + const HChar* colon = VG_(strchr)(p, ':'); + SizeT dirlen = colon ? (SizeT)(colon - p) : VG_(strlen)(p); + + dimg = try_buildid_dir(p, dirlen, buildid, rel_ok, &debugpath); + + p = colon ? colon + 1 : p + dirlen; + } - VG_(sprintf)(debugpath, "/usr/lib/debug/.build-id/%c%c/%s.debug", - buildid[0], buildid[1], buildid + 2); + if (dimg == NULL && extrapath != NULL) { + dimg = try_buildid_dir(extrapath, VG_(strlen)(extrapath), + buildid, rel_ok, &debugpath); + } + + if (dimg == NULL) { + debugpath = ML_(dinfo_zalloc)("di.fdf.1", + VG_(strlen)(buildid) + 33); - dimg = open_debug_file(debugpath, buildid, 0, rel_ok, NULL); - if (!dimg) { - ML_(dinfo_free)(debugpath); - debugpath = NULL; + VG_(sprintf)(debugpath, "/usr/lib/debug/.build-id/%c%c/%s.debug", + buildid[0], buildid[1], buildid + 2); + + dimg = open_debug_file(debugpath, buildid, 0, rel_ok, NULL); + if (!dimg) { + ML_(dinfo_free)(debugpath); + debugpath = NULL; + } } }