async-backtrace-0.2.7/.cargo_vcs_info.json0000644000000001470000000000100140760ustar { "git": { "sha1": "3dcc5ad3c0c3e2dce2fcf513dcec3ae5448597cb" }, "path_in_vcs": "backtrace" }async-backtrace-0.2.7/Cargo.lock0000644000000721060000000000100120550ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "addr2line" version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ "gimli", ] [[package]] name = "adler" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aho-corasick" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] [[package]] name = "async-backtrace" version = "0.2.7" dependencies = [ "async-backtrace-attributes", "core_affinity", "criterion", "dashmap", "futures", "loom", "once_cell", "pin-project-lite", "pretty_assertions", "regex", "rustc-hash", "static_assertions", "tokio", ] [[package]] name = "async-backtrace-attributes" version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "affbba0d438add06462a0371997575927bc05052f7ec486e7a4ca405c956c3d7" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ "hermit-abi 0.1.19", "libc", "winapi 0.3.9", ] [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" dependencies = [ "addr2line", "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", ] [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bumpalo" version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "cast" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ "libc", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ "bitflags", "textwrap", "unicode-width", ] [[package]] name = "core_affinity" version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f8a03115cc34fb0d7c321dd154a3914b3ca082ccc5c11d91bf7117dbbe7171f" dependencies = [ "kernel32-sys", "libc", "num_cpus", "winapi 0.2.8", ] [[package]] name = "criterion" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f" dependencies = [ "atty", "cast", "clap", "criterion-plot", "csv", "itertools", "lazy_static", "num-traits", "oorandom", "plotters", "rayon", "regex", "serde", "serde_cbor", "serde_derive", "serde_json", "tinytemplate", "walkdir", ] [[package]] name = "criterion-plot" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876" dependencies = [ "cast", "itertools", ] [[package]] name = "crossbeam-deque" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "csv" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" dependencies = [ "csv-core", "itoa", "ryu", "serde", ] [[package]] name = "csv-core" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" dependencies = [ "memchr", ] [[package]] name = "dashmap" version = "5.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", "hashbrown", "lock_api", "once_cell", "parking_lot_core", ] [[package]] name = "diff" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] name = "either" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "futures" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", "futures-executor", "futures-io", "futures-sink", "futures-task", "futures-util", ] [[package]] name = "futures-channel" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", ] [[package]] name = "futures-core" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", "futures-util", ] [[package]] name = "futures-io" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "futures-sink" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", "futures-io", "futures-macro", "futures-sink", "futures-task", "memchr", "pin-project-lite", "pin-utils", "slab", ] [[package]] name = "generator" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e" dependencies = [ "cc", "libc", "log", "rustversion", "windows", ] [[package]] name = "gimli" version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "half" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" [[package]] name = "hashbrown" version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" [[package]] name = "hermit-abi" version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ "libc", ] [[package]] name = "hermit-abi" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0c62115964e08cb8039170eb33c1d0e2388a256930279edca206fff675f82c3" [[package]] name = "itertools" version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] [[package]] name = "itoa" version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "js-sys" version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" dependencies = [ "wasm-bindgen", ] [[package]] name = "kernel32-sys" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" dependencies = [ "winapi 0.2.8", "winapi-build", ] [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "lock_api" version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", ] [[package]] name = "log" version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "loom" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" dependencies = [ "cfg-if", "generator", "scoped-tls", "tracing", "tracing-subscriber", ] [[package]] name = "matchers" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" dependencies = [ "regex-automata 0.1.10", ] [[package]] name = "memchr" version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "miniz_oxide" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", ] [[package]] name = "nu-ansi-term" version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" dependencies = [ "overload", "winapi 0.3.9", ] [[package]] name = "num-traits" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ "hermit-abi 0.3.5", "libc", ] [[package]] name = "object" version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] [[package]] name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oorandom" version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "overload" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "parking_lot_core" version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", "windows-targets", ] [[package]] name = "pin-project-lite" version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "plotters" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" dependencies = [ "num-traits", "plotters-backend", "plotters-svg", "wasm-bindgen", "web-sys", ] [[package]] name = "plotters-backend" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" [[package]] name = "plotters-svg" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" dependencies = [ "plotters-backend", ] [[package]] name = "pretty_assertions" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" dependencies = [ "diff", "yansi", ] [[package]] name = "proc-macro2" version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] [[package]] name = "rayon" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051" dependencies = [ "either", "rayon-core", ] [[package]] name = "rayon-core" version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ "crossbeam-deque", "crossbeam-utils", ] [[package]] name = "redox_syscall" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ "bitflags", ] [[package]] name = "regex" version = "1.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" dependencies = [ "aho-corasick", "memchr", "regex-automata 0.4.5", "regex-syntax 0.8.2", ] [[package]] name = "regex-automata" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ "regex-syntax 0.6.29", ] [[package]] name = "regex-automata" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" dependencies = [ "aho-corasick", "memchr", "regex-syntax 0.8.2", ] [[package]] name = "regex-syntax" version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "rustc-demangle" version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustc-hash" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustversion" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "ryu" version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" [[package]] name = "same-file" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ "winapi-util", ] [[package]] name = "scoped-tls" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" version = "1.0.196" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" dependencies = [ "serde_derive", ] [[package]] name = "serde_cbor" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" dependencies = [ "half", "serde", ] [[package]] name = "serde_derive" version = "1.0.196" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.113" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "sharded-slab" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ "lazy_static", ] [[package]] name = "slab" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "smallvec" version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "syn" version = "2.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "textwrap" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" dependencies = [ "unicode-width", ] [[package]] name = "thread_local" version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ "cfg-if", "once_cell", ] [[package]] name = "tinytemplate" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" dependencies = [ "serde", "serde_json", ] [[package]] name = "tokio" version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" dependencies = [ "backtrace", "num_cpus", "pin-project-lite", "tokio-macros", ] [[package]] name = "tokio-macros" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tracing" version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "pin-project-lite", "tracing-attributes", "tracing-core", ] [[package]] name = "tracing-attributes" version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tracing-core" version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", "valuable", ] [[package]] name = "tracing-log" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" dependencies = [ "log", "once_cell", "tracing-core", ] [[package]] name = "tracing-subscriber" version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ "matchers", "nu-ansi-term", "once_cell", "regex", "sharded-slab", "smallvec", "thread_local", "tracing", "tracing-core", "tracing-log", ] [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-width" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" [[package]] name = "valuable" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "walkdir" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" dependencies = [ "same-file", "winapi-util", ] [[package]] name = "wasm-bindgen" version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" dependencies = [ "cfg-if", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" dependencies = [ "proc-macro2", "quote", "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" [[package]] name = "web-sys" version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] name = "winapi" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-build" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ "winapi 0.3.9", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "yansi" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" async-backtrace-0.2.7/Cargo.toml0000644000000047050000000000100121000ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "async-backtrace" version = "0.2.7" description = "Efficient, logical 'backtraces' of async tasks." readme = "README.md" categories = [ "asynchronous", "development-tools::debugging", ] license = "MIT" repository = "https://github.com/tokio-rs/async-backtrace" [package.metadata.release] shared-version = true tag-name = "v{{version}}" [[package.metadata.release.pre-release-replacements]] file = "../CHANGELOG.md" replace = "{{version}}" search = "Unreleased" [[package.metadata.release.pre-release-replacements]] exactly = 1 file = "../CHANGELOG.md" replace = "...{{tag_name}}" search = '\.\.\.HEAD' [[package.metadata.release.pre-release-replacements]] file = "../CHANGELOG.md" replace = "{{date}}" search = "ReleaseDate" [[package.metadata.release.pre-release-replacements]] exactly = 1 file = "../CHANGELOG.md" replace = """ ## [Unreleased] - ReleaseDate""" search = "" [[package.metadata.release.pre-release-replacements]] exactly = 1 file = "../CHANGELOG.md" replace = """ [Unreleased]: https://github.com/tokio-rs/async-backtrace/compare/{{tag_name}}...HEAD""" search = "" [[bench]] name = "frame_overhead" harness = false [dependencies.async-backtrace-attributes] version = "0.2" [dependencies.dashmap] version = "5.4.0" [dependencies.futures] version = "0.3.21" [dependencies.once_cell] version = "1.0.0" [dependencies.pin-project-lite] version = "0.2" [dependencies.rustc-hash] version = "1.1.0" [dependencies.static_assertions] version = "1.1.0" [dev-dependencies.core_affinity] version = "0.5.10" [dev-dependencies.criterion] version = "0.3.4" features = ["html_reports"] [dev-dependencies.futures] version = "0.3.25" [dev-dependencies.pretty_assertions] version = "1.3.0" [dev-dependencies.regex] version = "1.6.0" [dev-dependencies.tokio] version = "1.21.2" features = [ "rt-multi-thread", "sync", "macros", ] [target."cfg(loom)".dependencies.loom] version = "0.5.6" async-backtrace-0.2.7/Cargo.toml.orig000064400000000000000000000031271046102023000155560ustar 00000000000000[package] name = "async-backtrace" version = "0.2.7" edition = "2018" license = "MIT" readme = "../README.md" description = "Efficient, logical 'backtraces' of async tasks." repository = "https://github.com/tokio-rs/async-backtrace" categories = ["asynchronous", "development-tools::debugging"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] async-backtrace-attributes = { version = "0.2", path = "../attributes" } dashmap = "5.4.0" futures = "0.3.21" once_cell = "1.0.0" pin-project-lite = "0.2" rustc-hash = "1.1.0" static_assertions = "1.1.0" [dev-dependencies] core_affinity = "0.5.10" criterion = { version = "0.3.4", features = ["html_reports"] } futures = "0.3.25" pretty_assertions = "1.3.0" regex = "1.6.0" tokio = { version = "1.21.2", features = ["rt-multi-thread", "sync", "macros"] } [target.'cfg(loom)'.dependencies] loom = "0.5.6" [[bench]] name = "frame_overhead" harness = false [package.metadata.release] shared-version = true pre-release-replacements = [ {file="../CHANGELOG.md", search="Unreleased", replace="{{version}}"}, {file="../CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1}, {file="../CHANGELOG.md", search="ReleaseDate", replace="{{date}}"}, {file="../CHANGELOG.md", search="", replace="\n\n## [Unreleased] - ReleaseDate", exactly=1}, {file="../CHANGELOG.md", search="", replace="\n[Unreleased]: https://github.com/tokio-rs/async-backtrace/compare/{{tag_name}}...HEAD", exactly=1}, ] tag-name = "v{{version}}" async-backtrace-0.2.7/LICENSE000064400000000000000000000020451046102023000136720ustar 00000000000000Copyright (c) 2022 Tokio Contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.async-backtrace-0.2.7/README.md000064400000000000000000000050621046102023000141460ustar 00000000000000 # async-backtrace Efficient, logical 'stack' traces of async functions. ## Usage To use, annotate your async functions with `#[async_backtrace::framed]`, like so: ```rust #[tokio::main] async fn main() { tokio::select! { _ = tokio::spawn(async_backtrace::frame!(pending())) => {} _ = foo() => {} }; } #[async_backtrace::framed] async fn pending() { std::future::pending::<()>().await } #[async_backtrace::framed] async fn foo() { bar().await; } #[async_backtrace::framed] async fn bar() { futures::join!(fiz(), buz()); } #[async_backtrace::framed] async fn fiz() { tokio::task::yield_now().await; } #[async_backtrace::framed] async fn buz() { println!("{}", baz().await); } #[async_backtrace::framed] async fn baz() -> String { async_backtrace::taskdump_tree(true) } ``` This example program will print out something along the lines of: ``` ╼ taskdump::foo::{{closure}} at backtrace/examples/taskdump.rs:20:1 └╼ taskdump::bar::{{closure}} at backtrace/examples/taskdump.rs:25:1 ├╼ taskdump::buz::{{closure}} at backtrace/examples/taskdump.rs:35:1 │ └╼ taskdump::baz::{{closure}} at backtrace/examples/taskdump.rs:40:1 └╼ taskdump::fiz::{{closure}} at backtrace/examples/taskdump.rs:30:1 ╼ taskdump::pending::{{closure}} at backtrace/examples/taskdump.rs:15:1 ``` ## Minimizing Overhead To minimize overhead, ensure that futures you spawn with your async runtime are marked with `#[framed]`. In other words, avoid doing this: ```rust tokio::spawn(async { foo().await; bar().await; }).await; #[async_backtrace::framed] async fn foo() {} #[async_backtrace::framed] async fn bar() {} ``` ...and prefer doing this: ```rust tokio::spawn(async_backtrace::location!().frame(async { foo().await; bar().await; })).await; #[async_backtrace::framed] async fn foo() {} #[async_backtrace::framed] async fn bar() {} ``` ## Estimating Overhead To estimate the overhead of adopting `#[framed]` in your application, refer to the benchmarks and interpretive guidance in `./backtrace/benches/frame_overhead.rs`. You can run these benchmarks with `cargo bench`. ## License This project is licensed under the [MIT license]. [MIT license]: https://github.com/tokio-rs/async-backtrace/blob/main/LICENSE ### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in async-backtrace by you, shall be licensed as MIT, without any additional terms or conditions. async-backtrace-0.2.7/benches/frame_overhead.rs000064400000000000000000000155421046102023000176170ustar 00000000000000use criterion::{ black_box, criterion_group, criterion_main, measurement::Measurement, BenchmarkGroup, Criterion, }; use std::time::Duration; macro_rules! parbench { ($b:expr; setup { $($setup:tt)* } bench { $($bench:tt)* }) => { $b.iter_custom(|iters| { use std::sync::{Arc, Barrier}; use std::time::{Duration, Instant}; let core_ids = core_affinity::get_core_ids().unwrap(); let num_cpus = core_ids.len(); let start = &Arc::new(Barrier::new(num_cpus + 1)); let stop = &Arc::new(Barrier::new(num_cpus + 1)); let mut workers: Vec<_> = core_ids.into_iter().map(|core_id| { let (start, stop) = (start.clone(), stop.clone()); std::thread::spawn(move || { core_affinity::set_for_current(core_id); $($setup)* start.wait(); let start_time = Instant::now(); for _i in 0..iters { $($bench)* } let stop_time = Instant::now(); stop.wait(); stop_time - start_time }) }).collect(); start.wait(); stop.wait(); let elapsed: Duration = workers.drain(..).map(|w| w.join().unwrap()).sum(); elapsed / (num_cpus as u32) }); } } fn bench_frame_overhead(c: &mut Criterion) { let mut group = c.benchmark_group("`Frame` overhead"); bench_root_poll_first(&mut group); bench_root_poll_rest(&mut group); bench_subframe_poll_first(&mut group); bench_subframe_poll_rest(&mut group); group.finish(); } /// BNCHMRK-0 /// /// Benchmark a root `Frame`'s initialization, first invocation of `in_scope`, /// and invocation of `Drop`. /// /// The results of this benchmark should be interpreted as the near-worst-case /// overhead of spawning a `#[framed]` async function. /// /// A root `Frame` sits at the top of its execution tree. Upon the first /// invocation of `in_scope`, this `Frame` must insert itself into the global /// task set. Likewise, when the root `Frame` is dropped, it must remove itself /// from this global task set. If many tasks are being initialized /// simultaneously, in parallel, access to this set will be highly contended. /// /// In this near-worst-case benchmark scenario, all cores of the host /// repeatedly simultaneously create root `Frame`s, invoke `Frame::in_scope` /// once, and then drop them. fn bench_root_poll_first>(c: &mut BenchmarkGroup<'_, M>) { c.bench_function("Frame::in_scope + Drop (root, first)", move |b| { parbench! { b; setup {} bench { // initialize a `Frame` let frame = async_backtrace::ඞ::Frame::new(async_backtrace::location!()); tokio::pin!(frame); // invoke `Frame::in_scope` once let _ = black_box(frame.as_mut().in_scope(|| black_box(42))); // drop the `Frame` } } }); } /// BNCHMRK-1 /// /// Benchmark a root `Frame`'s subsequent invocations of `Frame::in_scope`. /// /// The results of this benchmark should be interpreted as the baseline overhead /// of polling a `#[framed]` task. /// /// The actual overhead will be slightly higher, for each sub-`#[framed]` future /// within the task (see "Frame::in_scope (subframe, first)" and /// "Frame::in_scope (subframe, rest)" to estimate the cost of sub-`#[framed]` /// functions). /// /// The actual overhead will be significantly higher when a blocking backtrace /// is requested. /// /// Besides managing insertion/removal from the global task set, root `Frame`s /// are also responsible for locking the mutex that guards their children. This /// lock is almost always uncontended (except when a blocking backtrace is /// requested). fn bench_root_poll_rest>(c: &mut BenchmarkGroup<'_, M>) { c.bench_function("Frame::in_scope (root, rest)", move |b| { parbench! { b; setup { // initialize a `Frame` let frame = async_backtrace::ඞ::Frame::new(async_backtrace::location!()); tokio::pin!(frame); // invoke `Frame::in_scope` once let _ = black_box(frame.as_mut().in_scope(|| black_box(42))); } bench { // repeatedly invoke `Frame::in_scope` let _ = black_box(frame.as_mut().in_scope(|| black_box(42))); } } }); } /// BNCHMRK-2 /// /// Benchmark a sub-`Frame`'s first invocation of `in_scope`. /// /// The results of this benchmark reflect the worst-case cost of polling /// sub-`#[framed]` functions. It should be *very* cheap. /// /// Upon a sub-`#[framed]` future's first poll, the `Frame` must initialize /// itself, identifying its parent by reading a thread-local variable, and /// notifying its parent that it has a new child. This does not require any /// locking. fn bench_subframe_poll_first>(c: &mut BenchmarkGroup<'_, M>) { c.bench_function("Frame::in_scope (subframe, first)", move |b| { let root = async_backtrace::ඞ::Frame::new(async_backtrace::location!()); tokio::pin!(root); root.in_scope(|| { // within the scope of a root `Frame`, benchmark: b.iter(|| { // ...initializing a sub-`Frame`, let frame = async_backtrace::ඞ::Frame::new(async_backtrace::location!()); tokio::pin!(frame); // ...and invoking `Frame::in_scope` once on it. let _ = black_box(frame.as_mut().in_scope(|| black_box(42))); }) }); }); } /// BNCHMRK-3 /// /// Benchmark a sub-`Frame`'s subsequent invocations of `in_scope`. /// /// The results of this benchmark reflect the typical cost of polling /// sub-`#[framed]` functions. It should be virtually free. fn bench_subframe_poll_rest>(c: &mut BenchmarkGroup<'_, M>) { c.bench_function("Frame::in_scope (subframe, rest)", move |b| { let root = async_backtrace::ඞ::Frame::new(async_backtrace::location!()); tokio::pin!(root); root.in_scope(|| { // within the scope of a root `Frame`, initialize a subframe, let frame = async_backtrace::ඞ::Frame::new(async_backtrace::location!()); tokio::pin!(frame); // invoke `Frame::in_scope` on it let _ = black_box(frame.as_mut().in_scope(|| black_box(42))); // and benchmark subsequent invocations of `Frame::in_scope`. b.iter(|| { let _ = black_box(frame.as_mut().in_scope(|| black_box(42))); }) }); }); } criterion_group!(benches, bench_frame_overhead); criterion_main!(benches); async-backtrace-0.2.7/examples/join.rs000064400000000000000000000006471046102023000160160ustar 00000000000000//! Run this example to see how `join!` turns taskdumps into trees. #[tokio::main] async fn main() { joining().await; } #[async_backtrace::framed] async fn joining() { let (_, _) = tokio::join!(yielding(), ready()); } #[async_backtrace::framed] async fn yielding() { tokio::task::yield_now().await; } #[async_backtrace::framed] async fn ready() { println!("{}", async_backtrace::taskdump_tree(true)); } async-backtrace-0.2.7/examples/malformed.rs000064400000000000000000000015431046102023000170210ustar 00000000000000//! This example showcases what NOT to do. Avoid spawning tasks that are NOT //! annotated with `#[async_backtrace::framed]`. If such tasks are spawned, and //! they include invocations of functions that ARE annotated with //! `#[async_backtrace::framed]`, these sub-routines will appear as distinct //! tasks. This is both misleading, and more computationally expensive. //! //! Uncomment the attribute on `selecting()` to make this example behave well. #[tokio::main] async fn main() { selecting().await; } /* #[async_backtrace::framed] */ async fn selecting() { tokio::select! { biased; _ = yielding() => {} _ = ready() => {} }; } #[async_backtrace::framed] async fn yielding() { tokio::task::yield_now().await; } #[async_backtrace::framed] async fn ready() { println!("{}", async_backtrace::taskdump_tree(true)); } async-backtrace-0.2.7/examples/missing.rs000064400000000000000000000006351046102023000165250ustar 00000000000000//! Run this example to see how functions NOT annotated with //! `#[async_backtrace::framed]` don't appear in taskdumps. #[tokio::main] async fn main() { foo().await; } #[async_backtrace::framed] async fn foo() { bar().await; } /* #[async_backtrace::framed] */ async fn bar() { baz().await; } #[async_backtrace::framed] async fn baz() { println!("{}", async_backtrace::taskdump_tree(true)); } async-backtrace-0.2.7/examples/multiple.rs000064400000000000000000000014021046102023000167000ustar 00000000000000//! Run this example to see how taskdumps appear with multiple tasks. #[tokio::main(flavor = "current_thread")] async fn main() { tokio::select! { // run the following branches in order of their appearance biased; // spawn task #1 _ = tokio::spawn(foo()) => { unreachable!() } // spawn task #2 _ = tokio::spawn(foo()) => { unreachable!() } // print the running tasks _ = tokio::spawn(async {}) => { println!("{}", async_backtrace::taskdump_tree(true)); } }; } #[async_backtrace::framed] async fn foo() { bar().await; } #[async_backtrace::framed] async fn bar() { baz().await; } #[async_backtrace::framed] async fn baz() { std::future::pending::<()>().await } async-backtrace-0.2.7/examples/select.rs000064400000000000000000000007711046102023000163340ustar 00000000000000//! Run this example to see how `select!` turns taskdumps into trees. #[tokio::main] async fn main() { selecting().await; } #[async_backtrace::framed] async fn selecting() { tokio::select! { biased; _ = yielding() => {} _ = yielding() => {} _ = ready() => {} }; } #[async_backtrace::framed] async fn yielding() { tokio::task::yield_now().await; } #[async_backtrace::framed] async fn ready() { println!("{}", async_backtrace::taskdump_tree(true)); } async-backtrace-0.2.7/examples/taskdump.rs000064400000000000000000000021661046102023000167050ustar 00000000000000// This example outputs something like: // ╼ taskdump::foo::{{closure}} at backtrace/examples/taskdump.rs:20:1 // └╼ taskdump::bar::{{closure}} at backtrace/examples/taskdump.rs:25:1 // ├╼ taskdump::buz::{{closure}} at backtrace/examples/taskdump.rs:35:1 // │ └╼ taskdump::baz::{{closure}} at backtrace/examples/taskdump.rs:40:1 // └╼ taskdump::fiz::{{closure}} at backtrace/examples/taskdump.rs:30:1 // ╼ taskdump::pending::{{closure}} at backtrace/examples/taskdump.rs:15:1 #[tokio::main] async fn main() { tokio::select! { _ = tokio::spawn(pending()) => {} _ = foo() => {} }; } #[async_backtrace::framed] async fn pending() { std::future::pending::<()>().await } #[async_backtrace::framed] async fn foo() { bar().await; } #[async_backtrace::framed] async fn bar() { futures::join!(fiz(), buz()); } #[async_backtrace::framed] async fn fiz() { tokio::task::yield_now().await; } #[async_backtrace::framed] async fn buz() { println!("{}", baz().await); } #[async_backtrace::framed] async fn baz() -> String { async_backtrace::taskdump_tree(true) } async-backtrace-0.2.7/src/frame.rs000064400000000000000000000376441046102023000151310ustar 00000000000000use std::{iter::FusedIterator, marker::PhantomPinned, pin::Pin, ptr::NonNull}; use crate::{ cell::{Cell, UnsafeCell}, linked_list, sync::Mutex, Location, }; pin_project_lite::pin_project! { /// A [`Frame`] in an intrusive, doubly-linked tree of [`Frame`]s. pub struct Frame { // The location associated with this frame. location: Location, // The kind of this frame — either a root or a node. kind: Kind, // The children of this frame. children: UnsafeCell, // The siblings of this frame. #[pin] siblings: Siblings, // Since `Frame` is part of an intrusive linked list, it must remain pinned. _pinned: PhantomPinned, } impl PinnedDrop for Frame { fn drop(this: Pin<&mut Self>) { // If this frame has not yet been initialized, there's no need to do anything special upon drop. if this.is_uninitialized() { return; } let this = this.into_ref().get_ref(); if let Some(parent) = this.parent() { // remove this frame as a child of its parent unsafe { parent.children.with_mut(|children| (*children).remove(this.into())); } } else { // this is a task; deregister it crate::tasks::deregister(this); } } } } // It is safe to transfer a `Frame` across thread boundaries, as it does not // contain any pointers to thread-local storage, nor does it enable interior // mutation on shared pointers without locking. unsafe impl Send for Frame {} mod active_frame { use super::Frame; use crate::cell::Cell; use core::ptr::NonNull; #[cfg(loom)] loom::thread_local! { /// The [`Frame`] of the currently-executing [traced future](crate::Traced) (if any). static ACTIVE_FRAME: crate::cell::Cell>> = Cell::new(None); } #[cfg(not(loom))] std::thread_local! { /// The [`Frame`] of the currently-executing [traced future](crate::Traced) (if any). #[allow(clippy::declare_interior_mutable_const)] static ACTIVE_FRAME: crate::cell::Cell>> = const { Cell::new(None) }; } /// By calling this function, you pinky-swear to ensure that the value of /// `ACTIVE_FRAME` is always a valid (dereferenceable) `NonNull`. pub(crate) unsafe fn with(f: F) -> R where F: FnOnce(&Cell>>) -> R, { ACTIVE_FRAME.with(f) } } /// The kind of a [`Frame`]. enum Kind { /// The frame is not yet initialized. Uninitialized, /// The frame is the root node in its tree. Root { /// This mutex must be locked when accessing the /// [children][Frame::children] or [siblings][Frame::siblings] of this /// frame. mutex: Mutex<()>, }, /// The frame is *not* the root node of its tree. Node { /// The parent of this frame. parent: NonNull, }, } /// The siblings of a frame. type Siblings = linked_list::Pointers; /// The children of a frame. type Children = linked_list::LinkedList::Target>; impl Frame { /// Construct a new, uninitialized `Frame`. pub fn new(location: Location) -> Self { Self { location, kind: Kind::Uninitialized, children: UnsafeCell::new(linked_list::LinkedList::new()), siblings: linked_list::Pointers::new(), _pinned: PhantomPinned, } } /// Runs a given function on this frame. /// /// If an invocation of `Frame::in_scope` is nested within `f`, those frames /// will be initialized with this frame as their parent. pub fn in_scope(self: Pin<&mut Self>, f: F) -> R where F: FnOnce() -> R, { // This non-generic preparation routine has been factored out of `in_scope`'s // body, so as to reduce the monomorphization burden on the compiler. // // The soundness of other routines in this module depend on this function *not* // being leaked from `in_scope`. In general, the drop-guard pattern cannot // safely and soundly be used for frame management. If we attempt to provide // such an API, we must ensure that unsoudness does not occur if child frames // are dropped before their parents, or if a drop-guard is held across an // `await` point. unsafe fn activate<'a>( mut frame: Pin<&'a mut Frame>, active: &'a Cell>>, ) -> impl Drop + 'a { // If needed, initialize this frame. if frame.is_uninitialized() { let maybe_parent = active.get().map(|parent| parent.as_ref()); frame.as_mut().initialize_unchecked(maybe_parent) } let frame = frame.into_ref().get_ref(); // If this is the root frame, lock its children. This lock is inherited by // `f()`. let maybe_mutex_guard = if let Kind::Root { mutex } = &frame.kind { // Ignore poisoning. This is fine, since absolutely nothing between this line, // and the execution of `drop(maybe_mutex_guard)` can unwind-panic, *except* for // the execution of the user-provided function `f`. An unwind-panic of `f` will // not make this crate's state inconsistent, since the parent frame is always // restored by the below invocation of `crate::defer` upon its drop. Some(match mutex.lock() { Ok(guard) => guard, Err(err) => err.into_inner(), }) } else { None }; // Replace the previously-active frame with this frame. let previously_active = active.replace(Some(frame.into())); // At the end of this scope, restore the previously-active frame. crate::defer(move || { active.set(previously_active); drop(maybe_mutex_guard); }) } unsafe { // SAFETY: We uphold `with`'s invariants by restoring the previously active // frame after the execution of `f()`. active_frame::with(|active| { // Activate this frame. let _restore = activate(self, active); // Finally, execute the given function. f() }) } } /// Produces a boxed slice over this frame's ancestors. pub fn backtrace_locations(&self) -> Box<[Location]> { let len = self.backtrace().count(); let mut vec = Vec::with_capacity(len); vec.extend(self.backtrace().map(Frame::location)); vec.into_boxed_slice() } /// Produces the [`Location`] associated with this frame. pub fn location(&self) -> Location { self.location } /// Produces `true` if this `Frame` is uninitialized, otherwise false. fn is_uninitialized(&self) -> bool { self.kind.is_uninitialized() } /// Initializes this frame, unconditionally. /// /// ## Safety /// This method must only be called, at most, once. #[inline(never)] unsafe fn initialize_unchecked(mut self: Pin<&mut Self>, maybe_parent: Option<&Frame>) { match maybe_parent { // This frame has no parent... None => { // ...it is the root of its tree, *self.as_mut().project().kind = Kind::root(); // ...and must be registered as a task. crate::tasks::register(self.into_ref().get_ref()); } // This frame has a parent... Some(parent) => { // ...it is not the root of its tree. *self.as_mut().project().kind = Kind::node(parent); // ...and its parent should be notified that is has a new child. let this = NonNull::from(self.into_ref().get_ref()); parent .children .with_mut(|children| (*children).push_front(this)); } }; } /// Executes the given function with a reference to the active frame on this /// thread (if any). pub fn with_active(f: F) -> R where F: FnOnce(Option<&Frame>) -> R, { Frame::with_active_cell(|cell| f(cell.get())) } pub(crate) fn with_active_cell(f: F) -> R where F: FnOnce(&Cell>) -> R, { #[allow(clippy::needless_lifetimes)] unsafe fn into_ref<'a, 'b>( cell: &'a Cell>>, ) -> &'a Cell> { // SAFETY: `Cell>` has the same layout has `Cell<&Frame>`, // because both `Cell` and `NonNull` are `#[repr(transparent)]`, and because // `*const Frame` has the same layout as `&Frame`. core::mem::transmute(cell) } unsafe { // SAFETY: We uphold `with`'s invariants, by only providing `f` with a // *reference* to the frame. active_frame::with(|cell| { let cell = into_ref(cell); f(cell) }) } } /// Produces the mutex (if any) guarding this frame's children. pub(crate) fn mutex(&self) -> Option<&Mutex<()>> { if let Kind::Root { mutex } = &self.kind { Some(mutex) } else { None } } pub(crate) unsafe fn fmt( &self, w: &mut W, subframes_locked: bool, ) -> std::fmt::Result { unsafe fn fmt_helper( f: &mut W, frame: &Frame, is_last: bool, prefix: &str, subframes_locked: bool, copies: usize, ) -> core::fmt::Result { let location = frame.location(); let current; let next; if is_last { if copies != 1 { current = format!("{prefix}└╼ {copies}x {location}"); } else { current = format!("{prefix}└╼ {location}"); } next = format!("{prefix} "); } else { if copies != 1 { current = format!("{prefix}├╼ {copies}x {location}"); } else { current = format!("{prefix}├╼ {location}"); } next = format!("{prefix}│ "); } // print all but the first three codepoints of current write!(f, "{}", { let mut current = current.chars(); current.next().unwrap(); current.next().unwrap(); current.next().unwrap(); ¤t.as_str() })?; if subframes_locked { let mut subframes = frame.subframes().peekable(); let mut copies = 1; while let Some(subframe) = subframes.next() { if subframes .peek() .map(|next| next.deep_eq(subframe)) .unwrap_or(false) { copies += 1; } else { writeln!(f)?; let is_last = subframes.peek().is_none(); fmt_helper(f, subframe, is_last, &next, true, copies)?; copies = 1; } } } else { writeln!(f)?; write!(f, "{prefix}└┈ [POLLING]")?; } Ok(()) } fmt_helper(w, self, true, " ", subframes_locked, 1) } /// Produces the parent frame of this frame. pub(crate) fn parent(&self) -> Option<&Frame> { if self.is_uninitialized() { None } else if let Kind::Node { parent } = self.kind { Some(unsafe { parent.as_ref() }) } else { None } } /// Produces the root frame of this futures tree. pub(crate) fn root(&self) -> &Frame { let mut frame = self; while let Some(parent) = frame.parent() { frame = parent; } frame } /// Produces an iterator over this frame's ancestors. pub fn backtrace(&self) -> impl FusedIterator { /// An iterator that traverses up the tree of [`Frame`]s from a leaf. #[derive(Clone)] pub(crate) struct Backtrace<'a> { frame: Option<&'a Frame>, } impl<'a> Backtrace<'a> { pub(crate) fn from_leaf(frame: &'a Frame) -> Self { Self { frame: Some(frame) } } } impl<'a> Iterator for Backtrace<'a> { type Item = &'a Frame; fn next(&mut self) -> Option { let curr = self.frame; self.frame = curr.and_then(Frame::parent); curr } } impl<'a> FusedIterator for Backtrace<'a> {} Backtrace::from_leaf(self) } /// Produces an iterator over this frame's children, in order from /// less-recently initialized to more recently initialized. /// /// # Safety /// The caller must ensure that the corresponding Kind::Root{mutex} is /// locked. The caller must also ensure that the returned iterator is /// dropped before the mutex is dropped. pub(crate) unsafe fn subframes(&self) -> impl FusedIterator { pub(crate) struct Subframes<'a> { iter: linked_list::Iter<'a, Frame>, } impl<'a> Subframes<'a> { pub(crate) unsafe fn from_parent(frame: &'a Frame) -> Self { Self { iter: frame.children.with(|children| (*children).iter()), } } } impl<'a> Iterator for Subframes<'a> { type Item = &'a Frame; fn next(&mut self) -> Option { self.iter.next().map(|frame| unsafe { frame.as_ref() }) } } impl<'a> FusedIterator for Subframes<'a> {} Subframes::from_parent(self) } /// # Safety /// The caller must ensure that the corresponding Kind::Root{mutex} is /// locked. pub(crate) unsafe fn deep_eq(&self, other: &Frame) -> bool { if self.location() != other.location() { return false; } let mut self_subframes = self.subframes(); let mut other_subframes = other.subframes(); loop { match (self_subframes.next(), other_subframes.next()) { (Some(self_subframe), Some(other_subframe)) => { if !self_subframe.deep_eq(other_subframe) { return false; } } (None, None) => { return true; } _ => { return false; } } } } } impl Kind { /// Produces a new [`Kind::Root`]. fn root() -> Self { Kind::Root { mutex: Mutex::new(()), } } /// Produces a new [`Kind::Node`]. fn node(parent: &Frame) -> Self { Kind::Node { parent: NonNull::from(parent), } } /// True if kind is [`Kind::Uninitialized`]. fn is_uninitialized(&self) -> bool { matches!(&self, Kind::Uninitialized) } } unsafe impl linked_list::Link for Frame { type Handle = NonNull; type Target = Self; fn as_raw(handle: &NonNull) -> NonNull { *handle } unsafe fn from_raw(ptr: NonNull) -> NonNull { ptr } unsafe fn pointers(target: NonNull) -> NonNull> { let me = target.as_ptr(); let field = ::std::ptr::addr_of_mut!((*me).siblings); NonNull::new_unchecked(field) } } async-backtrace-0.2.7/src/framed.rs000064400000000000000000000024071046102023000152620ustar 00000000000000use core::future::Future; use core::pin::Pin; use core::task::{Context, Poll}; use std::marker::PhantomPinned; use crate::frame::Frame; use crate::location::Location; use pin_project_lite::pin_project; pin_project! { /// A future whose [`Location`] is included in [taskdumps][crate::tasks] and [backtraces][crate::backtrace]. pub struct Framed { // The wrapped future. #[pin] future: F, // Metadata about the wrapped future. #[pin] frame: Frame, _pinned: PhantomPinned, } } impl core::panic::UnwindSafe for Framed {} impl Framed { /// Include the given `future` in taskdumps and /// backtraces with the given `location`. pub fn new(future: F, location: Location) -> Self { Self { future, frame: Frame::new(location), _pinned: PhantomPinned, } } } impl Future for Framed where F: Future, { type Output = ::Output; #[track_caller] fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<::Output> { let this = self.project(); let frame = this.frame; let future = this.future; frame.in_scope(|| future.poll(cx)) } } async-backtrace-0.2.7/src/lib.rs000064400000000000000000000155321046102023000145750ustar 00000000000000//! Efficient, logical 'stack' traces of async functions. //! //! ## Usage //! To use, annotate your async functions with `#[async_backtrace::framed]`, //! like so: //! //! ```rust //! #[tokio::main] //! async fn main() { //! tokio::select! { //! _ = tokio::spawn(async_backtrace::frame!(pending())) => {} //! _ = foo() => {} //! }; //! } //! //! #[async_backtrace::framed] //! async fn pending() { //! std::future::pending::<()>().await //! } //! //! #[async_backtrace::framed] //! async fn foo() { //! bar().await; //! } //! //! #[async_backtrace::framed] //! async fn bar() { //! futures::join!(fiz(), buz()); //! } //! //! #[async_backtrace::framed] //! async fn fiz() { //! tokio::task::yield_now().await; //! } //! //! #[async_backtrace::framed] //! async fn buz() { //! println!("{}", baz().await); //! } //! //! #[async_backtrace::framed] //! async fn baz() -> String { //! async_backtrace::taskdump_tree(true) //! } //! ``` //! //! This example program will print out something along the lines of: //! //! ```text //! ╼ taskdump::foo::{{closure}} at backtrace/examples/taskdump.rs:20:1 //! └╼ taskdump::bar::{{closure}} at backtrace/examples/taskdump.rs:25:1 //! ├╼ taskdump::buz::{{closure}} at backtrace/examples/taskdump.rs:35:1 //! │ └╼ taskdump::baz::{{closure}} at backtrace/examples/taskdump.rs:40:1 //! └╼ taskdump::fiz::{{closure}} at backtrace/examples/taskdump.rs:30:1 //! ╼ taskdump::pending::{{closure}} at backtrace/examples/taskdump.rs:15:1 //! ``` //! //! ## Minimizing Overhead //! To minimize overhead, ensure that futures you spawn with your async runtime //! are marked with `#[framed]`. //! //! In other words, avoid doing this: //! ```rust //! # #[tokio::main] async fn main() { //! tokio::spawn(async { //! foo().await; //! bar().await; //! }).await; //! # } //! //! #[async_backtrace::framed] async fn foo() {} //! #[async_backtrace::framed] async fn bar() {} //! ``` //! ...and prefer doing this: //! ```rust //! # #[tokio::main] async fn main() { //! tokio::spawn(async_backtrace::location!().frame(async { //! foo().await; //! bar().await; //! })).await; //! # } //! //! #[async_backtrace::framed] async fn foo() {} //! #[async_backtrace::framed] async fn bar() {} //! ``` //! //! ## Estimating Overhead //! To estimate the overhead of adopting `#[framed]` in your application, refer //! to the benchmarks and interpretive guidance in //! `./backtrace/benches/frame_overhead.rs`. You can run these benchmarks with //! `cargo bench`. pub(crate) mod frame; pub(crate) mod framed; pub(crate) mod linked_list; pub(crate) mod location; pub(crate) mod tasks; pub(crate) use frame::Frame; pub(crate) use framed::Framed; pub use location::Location; pub use tasks::{tasks, Task}; /// Include the annotated async function in backtraces and taskdumps. /// /// This, for instance: /// ``` /// # async fn bar() {} /// # async fn baz() {} /// #[async_backtrace::framed] /// async fn foo() { /// bar().await; /// baz().await; /// } /// ``` /// ...expands, roughly, to: /// ``` /// # async fn bar() {} /// # async fn baz() {} /// async fn foo() { /// async_backtrace::frame!(async move { /// bar().await; /// baz().await; /// }).await; /// } /// ``` pub use async_backtrace_attributes::framed; /// Include the annotated async expression in backtraces and taskdumps. /// /// This, for instance: /// ``` /// # #[tokio::main] async fn main() { /// # async fn foo() {} /// # async fn bar() {} /// tokio::spawn(async_backtrace::frame!(async { /// foo().await; /// bar().await; /// })).await; /// # } /// ``` /// ...expands, roughly, to: /// ``` /// # #[tokio::main] async fn main() { /// # async fn foo() {} /// # async fn bar() {} /// tokio::spawn(async_backtrace::location!().frame(async { /// foo().await; /// bar().await; /// })).await; /// # } /// ``` #[macro_export] macro_rules! frame { ($async_expr:expr) => { $crate::location!().frame($async_expr) }; } /// Produces a human-readable tree of task states. /// /// If `wait_for_running_tasks` is `false`, this routine will display only the /// top-level location of currently-running tasks and a note that they are /// "POLLING". Otherwise, this routine will wait for currently-running tasks to /// become idle. /// /// # Safety /// If `wait_for_running_tasks` is `true`, this routine may deadlock if any /// non-async lock is held which may also be held by a Framed task. pub fn taskdump_tree(wait_for_running_tasks: bool) -> String { tasks() .map(|task| task.pretty_tree(wait_for_running_tasks)) .collect::>() .join("\n") } /// Produces a backtrace starting at the currently-active frame (if any). /// /// ## Example /// ``` /// use async_backtrace::{framed, backtrace, Location}; /// /// #[tokio::main] /// async fn main() { /// foo().await; /// } /// /// #[async_backtrace::framed] /// async fn foo() { /// bar().await; /// } /// /// #[async_backtrace::framed] /// async fn bar() { /// baz().await; /// } /// /// #[async_backtrace::framed] /// async fn baz() { /// # macro_rules! assert_eq { ($l:expr, $r:expr) => { debug_assert_eq!($l.len(), $r.len());} } /// assert_eq!(&async_backtrace::backtrace().unwrap().iter().map(|l| l.to_string()).collect::>()[..], &[ /// "rust_out::baz::{{closure}} at src/lib.rs:19:1", /// "rust_out::bar::{{closure}} at src/lib.rs:14:1", /// "rust_out::foo::{{closure}} at src/lib.rs:9:1", /// ]); /// } /// ``` pub fn backtrace() -> Option> { Frame::with_active(|maybe_frame| maybe_frame.map(Frame::backtrace_locations)) } pub(crate) mod sync { #[cfg(loom)] pub(crate) use loom::sync::Mutex; #[cfg(not(loom))] pub(crate) use std::sync::Mutex; pub(crate) use std::sync::TryLockError; } pub(crate) mod cell { #[cfg(loom)] pub(crate) use loom::cell::{Cell, UnsafeCell}; #[cfg(not(loom))] pub(crate) use std::cell::Cell; #[cfg(not(loom))] #[derive(Debug)] #[repr(transparent)] pub(crate) struct UnsafeCell(std::cell::UnsafeCell); #[cfg(not(loom))] impl UnsafeCell { pub(crate) fn new(data: T) -> UnsafeCell { UnsafeCell(std::cell::UnsafeCell::new(data)) } pub(crate) fn with(&self, f: impl FnOnce(*const T) -> R) -> R { f(self.0.get()) } pub(crate) fn with_mut(&self, f: impl FnOnce(*mut T) -> R) -> R { f(self.0.get()) } } } pub(crate) fn defer R, R>(f: F) -> impl Drop { struct Defer R, R>(Option); impl R, R> Drop for Defer { fn drop(&mut self) { self.0.take().unwrap()(); } } Defer(Some(f)) } #[doc(hidden)] /** NOT STABLE! DO NOT USE! */ pub mod ඞ { // ^ kudos to Daniel Henry-Mantilla pub use crate::frame::Frame; } async-backtrace-0.2.7/src/linked_list.rs000064400000000000000000000243371046102023000163330ustar 00000000000000// Copyright (c) 2022 Tokio Contributors // // Permission is hereby granted, free of charge, to any // person obtaining a copy of this software and associated // documentation files (the "Software"), to deal in the // Software without restriction, including without // limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of // the Software, and to permit persons to whom the Software // is furnished to do so, subject to the following // conditions: // // The above copyright notice and this permission notice // shall be included in all copies or substantial portions // of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT // SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. //! An intrusive double linked list of data. //! //! The data structure supports tracking pinned nodes. Most of the data //! structure's APIs are `unsafe` as they require the caller to ensure the //! specified node is actually contained by the list. //! //! [Adapted from Tokio.](https://github.com/tokio-rs/tokio/blob/master/tokio/src/util/linked_list.rs) use crate::cell::UnsafeCell; use core::fmt; use core::marker::{PhantomData, PhantomPinned}; use core::mem::ManuallyDrop; use core::ptr::{self, NonNull}; /// An intrusive linked list. /// /// Currently, the list is not emptied on drop. It is the caller's /// responsibility to ensure the list is empty before dropping it. pub(crate) struct LinkedList { /// Linked list head head: Option>, /// Linked list tail tail: Option>, /// Node type marker. _marker: PhantomData<*const L>, } unsafe impl Send for LinkedList where L::Target: Send {} unsafe impl Sync for LinkedList where L::Target: Sync {} /// Defines how a type is tracked within a linked list. /// /// In order to support storing a single type within multiple lists, accessing /// the list pointers is decoupled from the entry type. /// /// # Safety /// /// Implementations must guarantee that `Target` types are pinned in memory. In /// other words, when a node is inserted, the value will not be moved as long as /// it is stored in the list. pub(crate) unsafe trait Link { /// Handle to the list entry. /// /// This is usually a pointer-ish type. type Handle; /// Node type. type Target; /// Convert the handle to a raw pointer without consuming the handle. #[allow(clippy::wrong_self_convention)] fn as_raw(handle: &Self::Handle) -> NonNull; /// Convert the raw pointer to a handle unsafe fn from_raw(ptr: NonNull) -> Self::Handle; /// Return the pointers for a node /// /// # Safety /// /// The resulting pointer should have the same tag in the stacked-borrows /// stack as the argument. In particular, the method may not create an /// intermediate reference in the process of creating the resulting raw /// pointer. unsafe fn pointers(target: NonNull) -> NonNull>; } /// Previous / next pointers. pub(crate) struct Pointers { inner: UnsafeCell>, } /// We do not want the compiler to put the `noalias` attribute on mutable /// references to this type, so the type has been made `!Unpin` with a /// `PhantomPinned` field. /// /// Additionally, we never access the `prev` or `next` fields directly, as any /// such access would implicitly involve the creation of a reference to the /// field, which we want to avoid since the fields are not `!Unpin`, and would /// hence be given the `noalias` attribute if we were to do such an access. /// As an alternative to accessing the fields directly, the `Pointers` type /// provides getters and setters for the two fields, and those are implemented /// using raw pointer casts and offsets, which is valid since the struct is /// #[repr(C)]. /// /// See this link for more information: /// #[repr(C)] struct PointersInner { /// The previous node in the list. null if there is no previous node. /// /// This field is accessed through pointer manipulation, so it is not dead /// code. #[allow(dead_code)] prev: Option>, /// The next node in the list. null if there is no previous node. /// /// This field is accessed through pointer manipulation, so it is not dead /// code. #[allow(dead_code)] next: Option>, /// This type is !Unpin due to the heuristic from: /// _pin: PhantomPinned, } unsafe impl Send for Pointers {} unsafe impl Sync for Pointers {} pub(crate) struct Iter<'a, T: Link> { _list: &'a LinkedList, curr: Option>, } // ===== impl LinkedList ===== impl LinkedList { /// Creates an empty linked list. pub(crate) const fn new() -> LinkedList { LinkedList { head: None, tail: None, _marker: PhantomData, } } pub(crate) fn iter(&self) -> Iter<'_, L> where L: Link, { Iter { _list: self, curr: self.head, } } } impl LinkedList { /// Adds an element first in the list. pub(crate) fn push_front(&mut self, val: L::Handle) { // The value should not be dropped, it is being inserted into the list let val = ManuallyDrop::new(val); let ptr = L::as_raw(&val); assert_ne!(self.head, Some(ptr)); unsafe { L::pointers(ptr).as_mut().set_next(self.head); L::pointers(ptr).as_mut().set_prev(None); if let Some(head) = self.head { L::pointers(head).as_mut().set_prev(Some(ptr)); } self.head = Some(ptr); if self.tail.is_none() { self.tail = Some(ptr); } } } /// Removes the specified node from the list /// /// # Safety /// /// The caller **must** ensure that `node` is currently contained by /// `self` or not contained by any other list. pub(crate) unsafe fn remove(&mut self, node: NonNull) -> Option { if let Some(prev) = L::pointers(node).as_ref().get_prev() { debug_assert_eq!(L::pointers(prev).as_ref().get_next(), Some(node)); L::pointers(prev) .as_mut() .set_next(L::pointers(node).as_ref().get_next()); } else { if self.head != Some(node) { return None; } self.head = L::pointers(node).as_ref().get_next(); } if let Some(next) = L::pointers(node).as_ref().get_next() { debug_assert_eq!(L::pointers(next).as_ref().get_prev(), Some(node)); L::pointers(next) .as_mut() .set_prev(L::pointers(node).as_ref().get_prev()); } else { // This might be the last item in the list if self.tail != Some(node) { return None; } self.tail = L::pointers(node).as_ref().get_prev(); } L::pointers(node).as_mut().set_next(None); L::pointers(node).as_mut().set_prev(None); Some(L::from_raw(node)) } } impl fmt::Debug for LinkedList { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("LinkedList") .field("head", &self.head) .field("tail", &self.tail) .finish() } } // ===== impl Pointers ===== impl Pointers { /// Create a new set of empty pointers pub(crate) fn new() -> Pointers { Pointers { inner: UnsafeCell::new(PointersInner { prev: None, next: None, _pin: PhantomPinned, }), } } pub(crate) fn get_prev(&self) -> Option> { // SAFETY: prev is the first field in PointersInner, which is #[repr(C)]. unsafe { self.inner.with(|inner| { let prev = inner as *const Option>; ptr::read(prev) }) } } pub(crate) fn get_next(&self) -> Option> { // SAFETY: next is the second field in PointersInner, which is #[repr(C)]. unsafe { self.inner.with(|inner| { let prev = inner as *const Option>; let next = prev.add(1); ptr::read(next) }) } } fn set_prev(&mut self, value: Option>) { // SAFETY: prev is the first field in PointersInner, which is #[repr(C)]. unsafe { self.inner.with_mut(|inner| { let prev = inner as *mut Option>; ptr::write(prev, value); }); } } fn set_next(&mut self, value: Option>) { // SAFETY: next is the second field in PointersInner, which is #[repr(C)]. unsafe { self.inner.with_mut(|inner| { let prev = inner as *mut Option>; let next = prev.add(1); ptr::write(next, value); }); } } } impl fmt::Debug for Pointers { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let prev = self.get_prev(); let next = self.get_next(); f.debug_struct("Pointers") .field("prev", &prev) .field("next", &next) .finish() } } // ===== impl Iter ===== impl<'a, T> Iterator for Iter<'a, T> where T: Link, { type Item = T::Handle; fn next(&mut self) -> Option { let curr = self.curr; self.curr = curr.and_then(|curr| unsafe { T::pointers(curr).as_ref() }.get_next()); curr.map(|curr| unsafe { T::from_raw(curr) }) } } async-backtrace-0.2.7/src/location.rs000064400000000000000000000065231046102023000156370ustar 00000000000000use std::fmt::Display; use futures::Future; /// Produces a [`Location`] when invoked in a function body. /// /// ``` /// use async_backtrace::{location, Location}; /// /// #[tokio::main] /// async fn main() { /// assert_eq!(location!().to_string(), "rust_out::main::{{closure}} at backtrace/src/location.rs:8:16"); /// /// async { /// assert_eq!(location!().to_string(), "rust_out::main::{{closure}}::{{closure}} at backtrace/src/location.rs:11:20"); /// }.await; /// /// (|| async { /// assert_eq!(location!().to_string(), "rust_out::main::{{closure}}::{{closure}}::{{closure}} at backtrace/src/location.rs:15:20"); /// })().await; /// } /// ``` #[macro_export] macro_rules! location { () => {{ macro_rules! fn_name { () => {{ fn type_name_of_val(_: &T) -> &'static str { core::any::type_name::() } type_name_of_val(&|| {}) .strip_suffix("::{{closure}}") .unwrap() }}; } $crate::Location::from_components(fn_name!(), &(file!(), line!(), column!())) }}; } /// A source code location in a function body. /// /// To construct a `Location`, use [`location!()`]. #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct Location { /// The name of the surrounding function. name: Option<&'static str>, /// The file name, line number, and column number on which the surrounding /// function is defined. rest: &'static (&'static str, u32, u32), } impl Location { /// **DO NOT USE!** The signature of this method may change between /// non-breaking releases. #[doc(hidden)] #[inline(always)] pub const fn from_components( name: &'static str, rest: &'static (&'static str, u32, u32), ) -> Self { Self { name: Some(name), rest, } } /// Include the given future in taskdumps with this location. /// /// ## Examples /// ``` /// # async fn bar() {} /// # async fn baz() {} /// async fn foo() { /// async_backtrace::location!().frame(async move { /// bar().await; /// baz().await; /// }).await /// } /// ``` pub fn frame(self, f: F) -> impl Future where F: Future, { crate::Framed::new(f, self) } /// Produces the function name associated with this location. pub const fn name(&self) -> Option<&str> { self.name } /// Produces the file name associated with this location. pub const fn file(&self) -> &str { self.rest.0 } /// Produces the line number associated with this location. pub const fn line(&self) -> u32 { self.rest.1 } /// Produces the column number associated with this location. pub const fn column(&self) -> u32 { self.rest.2 } } impl Display for Location { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let file = self.file(); let line = self.line(); let column = self.column(); if let Some(name) = self.name() { f.write_fmt(format_args!("{name} at {file}:{line}:{column}")) } else { f.write_fmt(format_args!("{file}:{line}:{column}")) } } } async-backtrace-0.2.7/src/tasks.rs000064400000000000000000000057761046102023000151650ustar 00000000000000use crate::Frame; use dashmap::DashSet as Set; use once_cell::sync::Lazy; use rustc_hash::FxHasher; use std::{hash::BuildHasherDefault, ops::Deref, ptr::NonNull}; /// A top-level [framed](crate::framed) future. #[derive(Hash, Eq, PartialEq)] #[repr(transparent)] pub struct Task(NonNull); unsafe impl Send for Task {} unsafe impl Sync for Task {} static TASK_SET: Lazy>> = Lazy::new(Set::default); /// Register a given root frame as a task. /// /// **SAFETY:** You vow to remove the given frame prior to it being dropped. pub(crate) unsafe fn register(root_frame: &Frame) { let unique = TASK_SET.insert(Task(NonNull::from(root_frame))); debug_assert!(unique); } /// De-register a given root frame as a task. pub(crate) fn deregister(root_frame: &Frame) { TASK_SET.remove(&Task(NonNull::from(root_frame))); } /// An iterator over tasks. /// /// **NOTE:** The creation and destruction of some or all tasks will be blocked /// for as long as the return value of this function is live. pub fn tasks() -> impl Iterator> { TASK_SET.iter() } impl Task { /// The location of this task. pub fn location(&self) -> crate::Location { // safety: we promise to not inspect the subframes without first locking let frame = unsafe { self.0.as_ref() }; frame.location() } /// Pretty-prints this task as a tree. /// /// If `block_until_idle` is `true`, this routine will block until the task /// is no longer being polled. In this case, the caller should not hold any /// locks which might be held by the task, otherwise deadlock may occur. /// /// If `block_until_idle` is `false`, and the task is being polled, the /// output will not include the sub-frames, instead simply note that the /// task is being polled. pub fn pretty_tree(&self, block_until_idle: bool) -> String { use crate::sync::TryLockError; // safety: we promise to not inspect the subframes without first locking let frame = unsafe { self.0.as_ref() }; let current_task: Option> = Frame::with_active(|maybe_frame| maybe_frame.map(|frame| frame.root().into())); let maybe_lock = &frame .mutex() // don't grab a lock if we're *in* the active task (it's already locked, then) .filter(|_| Some(self.0) != current_task) .map(|mutex| { if block_until_idle { mutex.lock().map_err(TryLockError::from) } else { mutex.try_lock() } }); let subframes_locked = match maybe_lock { None | Some(Ok(..)) => true, Some(Err(TryLockError::WouldBlock)) => false, Some(Err(err @ TryLockError::Poisoned(..))) => panic!("{:?}", err), }; let mut string = String::new(); unsafe { frame.fmt(&mut string, subframes_locked).unwrap(); } string } } async-backtrace-0.2.7/tests/consolidate.rs000064400000000000000000000020671046102023000167050ustar 00000000000000/// A test that taskdump_tree() consolidates adjacent identical subframes. mod util; #[test] fn consolidate() { util::model(|| util::run(selecting())); } #[async_backtrace::framed] async fn selecting() { tokio::select! { biased; _ = yielding_outer() => {} _ = yielding_outer() => {} _ = ready() => {} }; } #[async_backtrace::framed] async fn yielding_outer() { yielding_inner().await; } #[async_backtrace::framed] async fn yielding_inner() { tokio::task::yield_now().await; } #[async_backtrace::framed] async fn ready() { let dump = async_backtrace::taskdump_tree(true); pretty_assertions::assert_str_eq!( util::strip(dump), "\ ╼ consolidate::selecting::{{closure}} at backtrace/tests/consolidate.rs:LINE:COL ├╼ consolidate::ready::{{closure}} at backtrace/tests/consolidate.rs:LINE:COL └╼ 2x consolidate::yielding_outer::{{closure}} at backtrace/tests/consolidate.rs:LINE:COL └╼ consolidate::yielding_inner::{{closure}} at backtrace/tests/consolidate.rs:LINE:COL" ); } async-backtrace-0.2.7/tests/contention.rs000064400000000000000000000012361046102023000165560ustar 00000000000000/// A test that async-backtrace is well-behaved under contention. /// /// In this test, two threads are spawned: /// 1. Thread 1 executes a `framed` future, which requests a blocking taskdump /// three times in different ways (immediately, in a sub-frame, and upon drop). /// 2. Thread 2 requests a blocking taskdump. mod util; use async_backtrace::framed; #[test] fn contention() { util::model(|| { let handle_a = util::thread::spawn(|| util::run(outer())); let handle_b = util::thread::spawn(|| async_backtrace::taskdump_tree(true)); handle_a.join().unwrap(); handle_b.join().unwrap(); }); } #[framed] pub async fn outer() {} async-backtrace-0.2.7/tests/deadlockless.rs000064400000000000000000000016421046102023000170340ustar 00000000000000/// A test that a non-blocking taskdump will not deadlock, even if requested /// from insided a framed future that spawns a scoped thread that requests the /// task dump. mod util; use async_backtrace::framed; #[framed] fn deadlockless() { util::model(|| util::run(outer())) } #[framed] async fn outer() { let dump = std::thread::spawn(|| async_backtrace::taskdump_tree(true)) .join() .unwrap(); pretty_assertions::assert_str_eq!( util::strip(dump), "\ ╼ deadlockless::outer at backtrace/tests/deadlockless.rs:LINE:COL └┈ [POLLING]" ); inner().await; } #[framed] async fn inner() { let dump = util::thread::spawn(|| async_backtrace::taskdump_tree(true)) .join() .unwrap(); pretty_assertions::assert_str_eq!( util::strip(dump), "\ ╼ deadlockless::outer at backtrace/tests/deadlockless.rs:LINE:COL └┈ [POLLING]" ); } async-backtrace-0.2.7/tests/poll-in-drop.rs000064400000000000000000000015241046102023000167120ustar 00000000000000/// A test that async-backtrace is well-behaved when frames are await'ed inside /// a drop guard. mod util; use async_backtrace::framed; #[test] fn poll_in_drop() { util::model(|| { let on_drop = util::defer(|| util::run(inner())); util::run(outer(on_drop)); }); #[allow(drop_bounds)] #[framed] async fn outer(defer: impl Drop) { let _defer = defer; } #[framed] async fn inner() { let dump = async_backtrace::taskdump_tree(true); pretty_assertions::assert_str_eq!(util::strip(dump), "\ ╼ poll_in_drop::poll_in_drop::outer>::{{closure}} at backtrace/tests/poll-in-drop.rs:LINE:COL └╼ poll_in_drop::poll_in_drop::inner::{{closure}} at backtrace/tests/poll-in-drop.rs:LINE:COL"); } } async-backtrace-0.2.7/tests/reentrant.rs000064400000000000000000000014521046102023000164000ustar 00000000000000/// A test that a blocking threaddump does not deadlock a program when requested /// from within a `framed` task. mod util; use async_backtrace::framed; #[test] fn reentrant() { util::model(|| util::run(outer())); } #[framed] async fn outer() { let dump = async_backtrace::taskdump_tree(true); pretty_assertions::assert_str_eq!( util::strip(dump), "\ ╼ reentrant::outer::{{closure}} at backtrace/tests/reentrant.rs:LINE:COL" ); inner().await; } #[framed] async fn inner() { let dump = async_backtrace::taskdump_tree(true); pretty_assertions::assert_str_eq!( util::strip(dump), "\ ╼ reentrant::outer::{{closure}} at backtrace/tests/reentrant.rs:LINE:COL └╼ reentrant::inner::{{closure}} at backtrace/tests/reentrant.rs:LINE:COL" ); } async-backtrace-0.2.7/tests/util/mod.rs000064400000000000000000000022501046102023000161270ustar 00000000000000#![allow(unused_imports, unused_variables, dead_code)] use std::{future::Future, sync::Mutex, task::Poll}; pub(crate) fn model(f: F) where F: Fn() + Sync + Send + 'static, { #[cfg(not(loom))] f(); #[cfg(loom)] loom::model(f); } pub(crate) mod thread { #[cfg(not(loom))] pub(crate) use std::thread::{spawn, yield_now}; #[cfg(loom)] pub(crate) use loom::thread::{spawn, yield_now}; } pub fn run(f: F) -> ::Output { use std::task::Context; let mut f = Box::pin(f); let waker = futures::task::noop_waker(); let mut cx = Context::from_waker(&waker); loop { match f.as_mut().poll(&mut cx) { Poll::Ready(v) => return v, Poll::Pending => thread::yield_now(), } } } pub fn strip(str: impl AsRef) -> String { let re = regex::Regex::new(r":\d+:\d+").unwrap(); re.replace_all(str.as_ref(), ":LINE:COL").to_string() } pub fn defer R, R>(f: F) -> impl Drop { Defer(Some(f)) } struct Defer R, R>(Option); impl R, R> Drop for Defer { fn drop(&mut self) { self.0.take().unwrap()(); } }