├── .rustfmt.toml ├── example_cropped.png ├── deny.toml ├── .gitignore ├── .github ├── dependabot.yml └── workflows │ └── ci.yml ├── LICENSE-MIT ├── Cargo.toml ├── src ├── bin │ ├── flamegraph.rs │ └── cargo-flamegraph.rs └── lib.rs ├── LICENSE-APACHE ├── README.md └── Cargo.lock /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | reorder_imports = true 2 | -------------------------------------------------------------------------------- /example_cropped.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/flamegraph-rs/flamegraph/HEAD/example_cropped.png -------------------------------------------------------------------------------- /deny.toml: -------------------------------------------------------------------------------- 1 | [licenses] 2 | allow = ["Apache-2.0", "BSD-3-Clause", "CDDL-1.0", "ISC", "MIT", "MPL-2.0", "Unicode-3.0"] 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | *svg 4 | perf* 5 | 6 | # IntelliJ IDE files 7 | .idea/ 8 | # MacOS desktop layout files 9 | .DS_Store 10 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | schedule: 6 | interval: weekly 7 | time: "13:00" 8 | open-pull-requests-limit: 99 9 | ignore: 10 | - dependency-name: anyhow 11 | versions: 12 | - "> 1.0.43, < 2" 13 | - dependency-name: inferno 14 | versions: 15 | - "> 0.10.0, < 0.11" 16 | - dependency-name: opener 17 | versions: 18 | - "> 0.5.0, < 0.6" 19 | - dependency-name: signal-hook 20 | versions: 21 | - "> 0.3.10, < 0.4" 22 | - package-ecosystem: github-actions 23 | directory: "/" 24 | schedule: 25 | interval: weekly 26 | time: "06:28" 27 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | build: 13 | strategy: 14 | matrix: 15 | os: 16 | - ubuntu-latest 17 | - macOS-latest 18 | - windows-latest 19 | runs-on: ${{ matrix.os }} 20 | steps: 21 | - uses: actions/checkout@v6 22 | - uses: dtolnay/rust-toolchain@stable 23 | with: 24 | toolchain: stable 25 | components: rustfmt, clippy 26 | - run: cargo clippy --all-targets --all-features -- -D warnings 27 | - run: cargo fmt -- --check 28 | 29 | audit: 30 | runs-on: ubuntu-latest 31 | steps: 32 | - uses: actions/checkout@v6 33 | - uses: EmbarkStudios/cargo-deny-action@v2 34 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Ferrous Systems GmbH 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "flamegraph" 3 | version = "0.6.10" 4 | edition = "2021" 5 | rust-version = "1.78" 6 | description = "A simple cargo subcommand for generating flamegraphs, using inferno under the hood" 7 | license = "MIT OR Apache-2.0" 8 | repository = "https://github.com/flamegraph-rs/flamegraph" 9 | keywords = ["perf", "flamegraph", "profiling", "cargo", "cargo-subcommand"] 10 | categories = ["command-line-utilities", "development-tools::profiling", "visualization", "development-tools::cargo-plugins"] 11 | readme = "README.md" 12 | 13 | [[bin]] 14 | name = "cargo-flamegraph" 15 | path = "src/bin/cargo-flamegraph.rs" 16 | 17 | [[bin]] 18 | name = "flamegraph" 19 | path = "src/bin/flamegraph.rs" 20 | 21 | [dependencies] 22 | anyhow = "1.0.43" 23 | cargo_metadata = "0.23" 24 | clap = { version = "4.0.11", features = ["derive"] } 25 | clap_complete = "4.0.2" 26 | indicatif = "0.18.0" 27 | inferno = { version = "0.12.2", default-features = false, features = ["multithreaded", "nameattr"] } 28 | opener = "0.8.1" 29 | shlex = "1.1.0" 30 | rustc-demangle = { version = "0.1", features = ["std"] } 31 | 32 | [target.'cfg(unix)'.dependencies] 33 | signal-hook = "0.3.10" 34 | 35 | [target.'cfg(windows)'.dependencies] 36 | blondie = "0.5.2" 37 | 38 | [profile.release.build-override] 39 | opt-level = 0 40 | -------------------------------------------------------------------------------- /src/bin/flamegraph.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use anyhow::anyhow; 4 | use clap::{CommandFactory, Parser}; 5 | use clap_complete::Shell; 6 | 7 | use flamegraph::Workload; 8 | 9 | #[derive(Debug, Parser)] 10 | #[clap(version)] 11 | struct Opt { 12 | /// Profile a running process by pid (comma separated list) 13 | #[clap(short, long, value_delimiter(','))] 14 | pid: Vec, 15 | 16 | /// Generate shell completions for the given shell. 17 | #[clap(long, value_name = "SHELL", exclusive(true))] 18 | completions: Option, 19 | 20 | #[clap(flatten)] 21 | graph: flamegraph::Options, 22 | 23 | /// Analyze an existing perf file 24 | #[clap(long = "perfdata", conflicts_with = "pid")] 25 | perf_file: Option, 26 | 27 | #[clap(last = true)] 28 | trailing_arguments: Vec, 29 | } 30 | 31 | fn main() -> anyhow::Result<()> { 32 | let opt = Opt::parse(); 33 | 34 | if let Some(shell) = opt.completions { 35 | clap_complete::generate( 36 | shell, 37 | &mut Opt::command(), 38 | "flamegraph", 39 | &mut std::io::stdout(), 40 | ); 41 | return Ok(()); 42 | } 43 | 44 | opt.graph.check()?; 45 | 46 | let workload = if let Some(perf_file) = opt.perf_file { 47 | Workload::ReadPerf(perf_file) 48 | } else { 49 | match (opt.pid.is_empty(), opt.trailing_arguments.is_empty()) { 50 | (false, true) => Workload::Pid(opt.pid), 51 | (true, false) => Workload::Command(opt.trailing_arguments.clone()), 52 | (false, false) => return Err(anyhow!("cannot pass in command with --pid")), 53 | (true, true) => return Err(anyhow!("no workload given to generate a flamegraph for")), 54 | } 55 | }; 56 | flamegraph::generate_flamegraph_for_workload(workload, opt.graph) 57 | } 58 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2019 Ferrous Systems GmbH 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/bin/cargo-flamegraph.rs: -------------------------------------------------------------------------------- 1 | use std::path::{Path, PathBuf}; 2 | 3 | use anyhow::{anyhow, Context}; 4 | use cargo_metadata::{Artifact, ArtifactDebuginfo, Message, MetadataCommand, Package, TargetKind}; 5 | use clap::{Args, Parser}; 6 | 7 | use flamegraph::Workload; 8 | 9 | #[derive(Debug, Clone, Copy, clap::ValueEnum)] 10 | #[clap(rename_all = "snake_case")] 11 | enum UnitTestTargetKind { 12 | Bin, 13 | Lib, 14 | } 15 | 16 | #[derive(Args, Debug)] 17 | struct Opt { 18 | /// Build with the dev profile 19 | #[clap(long)] 20 | dev: bool, 21 | 22 | /// Build with the specified profile 23 | #[clap(long)] 24 | profile: Option, 25 | 26 | /// package with the binary to run 27 | #[clap(short, long)] 28 | package: Option, 29 | 30 | /// Binary to run 31 | #[clap(short, long, group = "exec-args")] 32 | bin: Option, 33 | 34 | /// Build for the target triple 35 | #[clap(long, group = "exec-args")] 36 | target: Option, 37 | 38 | /// Example to run 39 | #[clap(long, group = "exec-args")] 40 | example: Option, 41 | 42 | /// Test binary to run (currently profiles the test harness and all tests in the binary) 43 | #[clap(long, group = "exec-args")] 44 | test: Option, 45 | 46 | /// Crate target to unit test, may be omitted if crate only has one target 47 | /// (currently profiles the test harness and all tests in the binary; test selection 48 | /// can be passed as trailing arguments after `--` as separator) 49 | #[clap(long, group = "exec-args")] 50 | unit_test: Option>, 51 | 52 | /// Kind of target (lib or bin) when running with which is may be 53 | /// required when we have two targets with the same name. 54 | #[clap(long)] 55 | unit_test_kind: Option, 56 | 57 | /// Crate target to unit benchmark, may be omitted if crate only has one target 58 | /// (currently profiles the test harness and all tests in the binary; test selection 59 | /// can be passed as trailing arguments after `--` as separator) 60 | #[clap(long, group = "exec-args")] 61 | unit_bench: Option>, 62 | 63 | /// Benchmark to run 64 | #[clap(long, group = "exec-args")] 65 | bench: Option, 66 | 67 | /// Path to Cargo.toml 68 | #[clap(long)] 69 | manifest_path: Option, 70 | 71 | /// Build features to enable 72 | #[clap(short, long)] 73 | features: Option, 74 | 75 | /// Disable default features 76 | #[clap(long)] 77 | no_default_features: bool, 78 | 79 | /// No-op. For compatibility with `cargo run --release`. 80 | #[clap(short, long)] 81 | release: bool, 82 | 83 | #[clap(flatten)] 84 | graph: flamegraph::Options, 85 | 86 | /// Trailing arguments passed to the binary being profiled. 87 | #[clap(last = true)] 88 | trailing_arguments: Vec, 89 | } 90 | 91 | #[derive(Parser, Debug)] 92 | #[clap(bin_name = "cargo")] 93 | enum Cli { 94 | /// A cargo subcommand for generating flamegraphs, using inferno 95 | #[clap(version)] 96 | Flamegraph(Opt), 97 | } 98 | 99 | fn build(opt: &Opt, kind: Vec) -> anyhow::Result> { 100 | use std::process::{Command, Output, Stdio}; 101 | let mut cmd = Command::new("cargo"); 102 | 103 | // This will build benchmarks with the `bench` profile. This is needed 104 | // because the `--profile` argument for `cargo build` is unstable. 105 | if !opt.dev && (opt.bench.is_some() || opt.unit_bench.is_some()) { 106 | cmd.args(["bench", "--no-run"]); 107 | } else if opt.unit_test.is_some() { 108 | cmd.args(["test", "--no-run"]); 109 | } else { 110 | cmd.arg("build"); 111 | } 112 | 113 | if let Some(profile) = &opt.profile { 114 | cmd.arg("--profile").arg(profile); 115 | } else if !opt.dev && opt.bench.is_none() && opt.unit_bench.is_none() { 116 | // do not use `--release` when we are building for `bench` 117 | cmd.arg("--release"); 118 | } 119 | 120 | if let Some(ref package) = opt.package { 121 | cmd.arg("--package"); 122 | cmd.arg(package); 123 | } 124 | 125 | if let Some(ref bin) = opt.bin { 126 | cmd.arg("--bin"); 127 | cmd.arg(bin); 128 | } 129 | 130 | if let Some(ref target) = opt.target { 131 | cmd.arg("--target"); 132 | cmd.arg(target); 133 | } 134 | 135 | if let Some(ref example) = opt.example { 136 | cmd.arg("--example"); 137 | cmd.arg(example); 138 | } 139 | 140 | if let Some(ref test) = opt.test { 141 | cmd.arg("--test"); 142 | cmd.arg(test); 143 | } 144 | 145 | if let Some(ref bench) = opt.bench { 146 | cmd.arg("--bench"); 147 | cmd.arg(bench); 148 | } 149 | 150 | if let Some(Some(ref unit_test)) = opt.unit_test { 151 | match kind.iter().any(|k| k == &TargetKind::Lib) { 152 | true => cmd.arg("--lib"), 153 | false => cmd.args(["--bin", unit_test]), 154 | }; 155 | } 156 | 157 | if let Some(Some(ref unit_bench)) = opt.unit_bench { 158 | match kind.iter().any(|k| k == &TargetKind::Lib) { 159 | true => cmd.arg("--lib"), 160 | false => cmd.args(["--bin", unit_bench]), 161 | }; 162 | } 163 | 164 | if let Some(ref manifest_path) = opt.manifest_path { 165 | cmd.arg("--manifest-path"); 166 | cmd.arg(manifest_path); 167 | } 168 | 169 | if let Some(ref features) = opt.features { 170 | cmd.arg("--features"); 171 | cmd.arg(features); 172 | } 173 | 174 | if opt.no_default_features { 175 | cmd.arg("--no-default-features"); 176 | } 177 | 178 | cmd.arg("--message-format=json-render-diagnostics"); 179 | 180 | if opt.graph.verbose { 181 | println!("build command: {:?}", cmd); 182 | } 183 | 184 | let Output { status, stdout, .. } = cmd 185 | .stderr(Stdio::inherit()) 186 | .output() 187 | .context("failed to execute cargo build command")?; 188 | 189 | if !status.success() { 190 | return Err(anyhow!("cargo build failed")); 191 | } 192 | 193 | Message::parse_stream(&*stdout) 194 | .filter_map(|m| match m { 195 | Ok(Message::CompilerArtifact(artifact)) => Some(Ok(artifact)), 196 | Ok(_) => None, 197 | Err(e) => Some(Err(e).context("failed to parse cargo build output")), 198 | }) 199 | .collect() 200 | } 201 | 202 | fn workload(opt: &Opt, artifacts: &[Artifact]) -> anyhow::Result> { 203 | let mut trailing_arguments = opt.trailing_arguments.clone(); 204 | 205 | if artifacts.iter().all(|a| a.executable.is_none()) { 206 | return Err(anyhow!( 207 | "build artifacts do not contain any executable to profile" 208 | )); 209 | } 210 | 211 | let (kind, target): (&[TargetKind], _) = match opt { 212 | Opt { bin: Some(t), .. } => (&[TargetKind::Bin], t), 213 | Opt { 214 | example: Some(t), .. 215 | } => (&[TargetKind::Example], t), 216 | Opt { test: Some(t), .. } => (&[TargetKind::Test], t), 217 | Opt { bench: Some(t), .. } => (&[TargetKind::Bench], t), 218 | Opt { 219 | unit_test: Some(Some(t)), 220 | .. 221 | } => (&[TargetKind::Lib, TargetKind::Bin], t), 222 | Opt { 223 | unit_bench: Some(Some(t)), 224 | .. 225 | } => { 226 | trailing_arguments.push("--bench".to_string()); 227 | (&[TargetKind::Lib, TargetKind::Bin], t) 228 | } 229 | _ => return Err(anyhow!("no target for profiling")), 230 | }; 231 | 232 | // `target.kind` is a `Vec`, but it always seems to contain exactly one element. 233 | let (debug_level, binary_path) = artifacts 234 | .iter() 235 | .find_map(|a| { 236 | a.executable 237 | .as_deref() 238 | .filter(|_| { 239 | a.target.name == *target && a.target.kind.iter().any(|k| kind.contains(k)) 240 | }) 241 | .map(|e| (&a.profile.debuginfo, e)) 242 | }) 243 | .ok_or_else(|| { 244 | let targets: Vec<_> = artifacts 245 | .iter() 246 | .map(|a| (&a.target.kind, &a.target.name)) 247 | .collect(); 248 | anyhow!( 249 | "could not find desired target {:?} in the targets for this crate: {:?}", 250 | (kind, target), 251 | targets 252 | ) 253 | })?; 254 | 255 | if !opt.dev && debug_level == &ArtifactDebuginfo::None { 256 | let profile = match opt 257 | .example 258 | .as_ref() 259 | .or(opt.bin.as_ref()) 260 | .or_else(|| opt.unit_test.as_ref().unwrap_or(&None).as_ref()) 261 | { 262 | // binaries, examples and unit tests use release profile 263 | Some(_) => "release", 264 | // tests use the bench profile in release mode. 265 | _ => "bench", 266 | }; 267 | 268 | eprintln!("\nWARNING: profiling without debuginfo. Enable symbol information by adding the following lines to Cargo.toml:\n"); 269 | eprintln!("[profile.{}]", profile); 270 | eprintln!("debug = true\n"); 271 | eprintln!("Or set this environment variable:\n"); 272 | eprintln!("CARGO_PROFILE_{}_DEBUG=true\n", profile.to_uppercase()); 273 | } 274 | 275 | let mut command = Vec::with_capacity(1 + trailing_arguments.len()); 276 | command.push(binary_path.to_string()); 277 | command.extend(trailing_arguments); 278 | Ok(command) 279 | } 280 | 281 | #[derive(Clone, Debug)] 282 | struct BinaryTarget { 283 | package: String, 284 | target: String, 285 | kind: Vec, 286 | } 287 | 288 | impl std::fmt::Display for BinaryTarget { 289 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 290 | write!(f, "target {} in package {}", self.target, self.package) 291 | } 292 | } 293 | 294 | pub fn find_crate_root(manifest_path: Option<&Path>) -> anyhow::Result { 295 | match manifest_path { 296 | Some(path) => { 297 | let path = path.parent().ok_or_else(|| { 298 | anyhow!( 299 | "the manifest path '{}' must point to a Cargo.toml file", 300 | path.display() 301 | ) 302 | })?; 303 | 304 | path.canonicalize().with_context(|| { 305 | anyhow!( 306 | "failed to canonicalize manifest parent directory '{}'\nHint: make sure your manifest path exists and points to a Cargo.toml file", 307 | path.display() 308 | ) 309 | }) 310 | } 311 | None => { 312 | let cargo_toml = "Cargo.toml"; 313 | let cwd = std::env::current_dir().context("failed to determine working directory")?; 314 | 315 | for current in cwd.ancestors() { 316 | if current.join(cargo_toml).exists() { 317 | return Ok(current.to_path_buf()); 318 | } 319 | } 320 | 321 | Err(anyhow!( 322 | "could not find '{}' in '{}' or any parent directory", 323 | cargo_toml, 324 | cwd.display() 325 | )) 326 | } 327 | } 328 | } 329 | 330 | fn find_unique_target( 331 | kind: &[TargetKind], 332 | pkg: Option<&str>, 333 | manifest_path: Option<&Path>, 334 | target_name: Option<&str>, 335 | ) -> anyhow::Result { 336 | let mut metadata_command = MetadataCommand::new(); 337 | metadata_command.no_deps(); 338 | if let Some(ref manifest_path) = manifest_path { 339 | metadata_command.manifest_path(manifest_path); 340 | } 341 | 342 | let crate_root = find_crate_root(manifest_path)?; 343 | 344 | let mut packages = metadata_command 345 | .exec() 346 | .context("failed to access crate metadata")? 347 | .packages 348 | .into_iter() 349 | .filter(|p| match pkg { 350 | Some(pkg) => pkg == *p.name, 351 | None => p.manifest_path.starts_with(&crate_root), 352 | }) 353 | .peekable(); 354 | 355 | if packages.peek().is_none() { 356 | return Err(match pkg { 357 | Some(pkg) => anyhow!("workspace has no package named {}", pkg), 358 | None => anyhow!( 359 | "failed to find any package in '{}' or below", 360 | crate_root.display() 361 | ), 362 | }); 363 | } 364 | 365 | let mut num_packages = 0; 366 | let mut is_default = false; 367 | 368 | let mut targets: Vec<_> = packages 369 | .flat_map(|p| { 370 | let Package { 371 | targets, 372 | name, 373 | default_run, 374 | .. 375 | } = p; 376 | num_packages += 1; 377 | if default_run.is_some() { 378 | is_default = true; 379 | } 380 | targets.into_iter().filter_map(move |t| { 381 | // Keep only targets that are of the right kind. 382 | if !t.kind.iter().any(|s| kind.contains(s)) { 383 | return None; 384 | } 385 | 386 | // When `default_run` is set, keep only the target with that name. 387 | match &default_run { 388 | Some(name) if name != &t.name => return None, 389 | _ => {} 390 | } 391 | 392 | match target_name { 393 | Some(name) if name != t.name => return None, 394 | _ => {} 395 | } 396 | 397 | Some(BinaryTarget { 398 | package: name.as_ref().to_owned(), 399 | target: t.name, 400 | kind: t.kind, 401 | }) 402 | }) 403 | }) 404 | .collect(); 405 | 406 | match targets.as_slice() { 407 | [_] => { 408 | let target = targets.remove(0); 409 | // If the selected target is the default_run of the only package, do not print a message. 410 | if num_packages != 1 || !is_default { 411 | eprintln!( 412 | "automatically selected {} as it is the only valid target", 413 | target 414 | ); 415 | } 416 | Ok(target) 417 | } 418 | [] => Err(anyhow!( 419 | "crate has no automatically selectable target:\nHint: try passing `--example ` \ 420 | or similar to choose a binary" 421 | )), 422 | _ => Err(anyhow!( 423 | "several possible targets found: {:#?}, please pass an explicit target.", 424 | targets 425 | )), 426 | } 427 | } 428 | 429 | fn main() -> anyhow::Result<()> { 430 | let Cli::Flamegraph(mut opt) = Cli::parse(); 431 | opt.graph.check()?; 432 | 433 | let kind = if opt.bin.is_none() 434 | && opt.bench.is_none() 435 | && opt.example.is_none() 436 | && opt.test.is_none() 437 | && opt.unit_test.is_none() 438 | && opt.unit_bench.is_none() 439 | { 440 | let target = find_unique_target( 441 | &[TargetKind::Bin], 442 | opt.package.as_deref(), 443 | opt.manifest_path.as_deref(), 444 | None, 445 | )?; 446 | opt.bin = Some(target.target); 447 | opt.package = Some(target.package); 448 | target.kind 449 | } else if let Some(unit_test) = opt.unit_test { 450 | let kinds = match opt.unit_test_kind { 451 | Some(UnitTestTargetKind::Bin) => &[TargetKind::Bin][..], // get slice to help type inference 452 | Some(UnitTestTargetKind::Lib) => &[TargetKind::Lib], 453 | None => &[TargetKind::Bin, TargetKind::Lib], 454 | }; 455 | 456 | let target = find_unique_target( 457 | kinds, 458 | opt.package.as_deref(), 459 | opt.manifest_path.as_deref(), 460 | unit_test.as_deref(), 461 | )?; 462 | opt.unit_test = Some(Some(target.target)); 463 | opt.package = Some(target.package); 464 | target.kind 465 | } else if let Some(unit_bench) = opt.unit_bench { 466 | let target = find_unique_target( 467 | &[TargetKind::Bin, TargetKind::Lib], 468 | opt.package.as_deref(), 469 | opt.manifest_path.as_deref(), 470 | unit_bench.as_deref(), 471 | )?; 472 | opt.unit_bench = Some(Some(target.target)); 473 | opt.package = Some(target.package); 474 | target.kind 475 | } else { 476 | Vec::new() 477 | }; 478 | 479 | let artifacts = build(&opt, kind)?; 480 | let workload = workload(&opt, &artifacts)?; 481 | flamegraph::generate_flamegraph_for_workload(Workload::Command(workload), opt.graph) 482 | } 483 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [cargo-]flamegraph 2 | 3 | [![colorized flamegraph output](example_cropped.png)](example.svg) 4 | 5 | A Rust-powered flamegraph generator with additional support for 6 | Cargo projects! It can be used to profile anything, 7 | not just Rust projects! No perl or pipes required <3 8 | 9 | Built on top of [@jonhoo's](https://github.com/jonhoo) wonderful [Inferno](https://github.com/jonhoo/inferno) all-rust flamegraph generation library! 10 | 11 | > [!TIP] 12 | > You might want to also try [samply](https://github.com/mstange/samply), which provides a more interactive UI 13 | > using a seamless integration with Firefox's Profiler web UI. It is also written in Rust and has better macOS support. 14 | 15 | ## Quick Start 16 | 17 | Install it, and run 18 | 19 | ```bash 20 | # Rust projects 21 | cargo flamegraph 22 | 23 | # Arbitrary binaries 24 | flamegraph -- /path/to/binary 25 | ``` 26 | 27 | How to use flamegraphs: [what's a flamegraph, and how can I use it to guide systems performance work?](#systems-performance-work-guided-by-flamegraphs) 28 | 29 | ## Installation 30 | 31 | \[cargo-\]flamegraph supports 32 | 33 | - [Linux](#linux): relies on `perf` 34 | - [MacOS](#macos): relies on `xctrace` 35 | - [Windows](#windows): native support with the [blondie](https://github.com/nico-abram/blondie) library; also works with `dtrace` on Windows 36 | 37 | `cargo install flamegraph` will make the `flamegraph` and `cargo-flamegraph` binaries available in 38 | your cargo binary directory. On most systems this is something like `~/.cargo/bin`. 39 | 40 | ## Linux 41 | 42 | **Note**: If you're using lld or mold on Linux, you must use the `--no-rosegment` flag. Otherwise perf will not be able to generate accurate stack traces ([explanation](https://crbug.com/919499#c16)). For example, for lld: 43 | 44 | ```toml 45 | [target.x86_64-unknown-linux-gnu] 46 | linker = "/usr/bin/clang" 47 | rustflags = ["-Clink-arg=-fuse-ld=lld", "-Clink-arg=-Wl,--no-rosegment"] 48 | ``` 49 | 50 | and for mold: 51 | 52 | ```toml 53 | [target.x86_64-unknown-linux-gnu] 54 | linker = "clang" 55 | rustflags = ["-Clink-arg=-fuse-ld=/usr/local/bin/mold", "-Clink-arg=-Wl,--no-rosegment"] 56 | ``` 57 | 58 | #### Debian (x86 and aarch) 59 | 60 | **Note**: Debian bullseye (the current stable version as of 2022) packages an outdated version of Rust which does not meet flamegraph's requirements. You should use [rustup](https://rustup.rs/) to install an up-to-date version of Rust, or upgrade to Debian bookworm (the current testing version) or newer. 61 | 62 | ```bash 63 | sudo apt install -y linux-perf 64 | ``` 65 | 66 | #### Ubuntu (x86) 67 | 68 | ```bash 69 | sudo apt install linux-tools-common linux-tools-generic linux-tools-`uname -r` 70 | ``` 71 | 72 | #### Ubuntu (aarch) 73 | 74 | ```bash 75 | sudo apt install linux-tools-common linux-tools-generic linux-tools-`uname -r` 76 | ``` 77 | 78 | **Note:** The perf binary is not packaged for all kernel versions. A check and workaround is below. 79 | 80 | ```bash 81 | # Check if the perf binary is missing: 82 | ls -l /usr/lib/linux-tools/`uname -r`/ | grep perf 83 | 84 | # If it is there, you can stop here. 85 | # If is missing, check if you have the tools for another kernel version installed: 86 | ls -l /usr/lib/linux-tools/ 87 | 88 | # If you do, check it contains perf: 89 | # (replace ) 90 | ls -l /usr/lib/linux-tools// 91 | 92 | # If you do, symlink it. This has been tested with 6.14.0-1014-aws running perf from 6.8.0-85-generic 93 | # (replace as before) 94 | sudo ln -s /usr/lib/linux-tools//perf /usr/lib/linux-tools/`uname -r`/perf 95 | ``` 96 | 97 | #### Ubuntu/Ubuntu MATE (Raspberry Pi) 98 | 99 | ```bash 100 | sudo apt install linux-tools-raspi 101 | ``` 102 | 103 | #### Pop!\_OS 104 | 105 | ```bash 106 | sudo apt install linux-tools-common linux-tools-generic 107 | ``` 108 | 109 | ## Windows 110 | 111 | #### Blondie Backend 112 | 113 | This is enabled by default. 114 | Windows is supported out-of-the-box, thanks to [Nicolas Abram](https://github.com/nico-abram)'s excellent [blondie](https://github.com/nico-abram/blondie) library. 115 | 116 | #### DTrace on Windows 117 | 118 | Alternatively, one can [install DTrace on Windows](https://learn.microsoft.com/en-us/windows-hardware/drivers/devtest/dtrace). If found, flamegraph will always prefer using `dtrace` over the built-in Windows support. 119 | 120 | ## Shell auto-completion 121 | 122 | At the moment, only `flamegraph` supports auto-completion. Supported shells are `bash`, `fish`, `zsh`, `powershell` and `elvish`. 123 | `cargo-flamegraph` does not support auto-completion because it is not as straight-forward to implement for custom cargo subcommands. See [#153](https://github.com/flamegraph-rs/flamegraph/pull/153) for details. 124 | 125 | How you enable auto-completion depends on your shell, e.g. 126 | 127 | ```bash 128 | flamegraph --completions bash > $XDG_CONFIG_HOME/bash_completion # or /etc/bash_completion.d/ 129 | ``` 130 | 131 | ## Examples 132 | 133 | ```bash 134 | # if you'd like to profile an arbitrary executable: 135 | flamegraph [-o my_flamegraph.svg] -- /path/to/my/binary --my-arg 5 136 | 137 | # or if the executable is already running, you can provide the PID via `-p` (or `--pid`) flag: 138 | flamegraph [-o my_flamegraph.svg] --pid 1337 139 | 140 | # NOTE: By default, perf tries to compute which functions are 141 | # inlined at every stack frame for every sample. This can take 142 | # a very long time (see https://github.com/flamegraph-rs/flamegraph/issues/74). 143 | # If you don't want this, you can pass --no-inline to flamegraph: 144 | flamegraph --no-inline [-o my_flamegraph.svg] /path/to/my/binary --my-arg 5 145 | 146 | # cargo support provided through the cargo-flamegraph binary! 147 | # defaults to profiling cargo run --release 148 | cargo flamegraph 149 | 150 | # by default, `--release` profile is used, 151 | # but you can override this: 152 | cargo flamegraph --dev 153 | 154 | # if you'd like to profile a specific binary: 155 | cargo flamegraph --bin=stress2 156 | 157 | # if you want to pass arguments as you would with cargo run: 158 | cargo flamegraph -- my-command --my-arg my-value -m -f 159 | 160 | # if you want to use interesting perf or dtrace options, use `-c` 161 | # this is handy for correlating things like branch-misses, cache-misses, 162 | # or anything else available via `perf list` or dtrace for your system 163 | cargo flamegraph -c "record -e branch-misses -c 100 --call-graph lbr -g" 164 | 165 | # Run criterion benchmark 166 | # Note that the last --bench is required for `criterion 0.3` to run in benchmark mode, instead of test mode. 167 | cargo flamegraph --bench some_benchmark --features some_features -- --bench 168 | 169 | cargo flamegraph --example some_example --features some_features 170 | 171 | # Profile unit tests. 172 | # Note that a separating `--` is necessary if `--unit-test` is the last flag. 173 | cargo flamegraph --unit-test -- test::in::package::with::single::crate 174 | cargo flamegraph --unit-test crate_name -- test::in::package::with::multiple:crate 175 | cargo flamegraph --unit-test --dev test::may::omit::separator::if::unit::test::flag::not::last::flag 176 | 177 | # Profile integration tests. 178 | cargo flamegraph --test test_name 179 | ``` 180 | 181 | ## Usage 182 | 183 | `flamegraph` is quite simple. `cargo-flamegraph` is more sophisticated: 184 | 185 | ``` 186 | Usage: cargo flamegraph [OPTIONS] [-- ...] 187 | 188 | Arguments: 189 | [TRAILING_ARGUMENTS]... Trailing arguments passed to the binary being profiled 190 | 191 | Options: 192 | --dev Build with the dev profile 193 | --profile Build with the specified profile 194 | -p, --package package with the binary to run 195 | -b, --bin Binary to run 196 | --example Example to run 197 | --test Test binary to run (currently profiles the test harness and all tests in the binary) 198 | --unit-test [] Crate target to unit test, may be omitted if crate only has one target (currently profiles the test harness and all tests in the binary; test selection can be passed as trailing arguments after `--` as separator) 199 | --bench Benchmark to run 200 | --manifest-path Path to Cargo.toml 201 | -f, --features Build features to enable 202 | --no-default-features Disable default features 203 | -r, --release No-op. For compatibility with `cargo run --release` 204 | -v, --verbose Print extra output to help debug problems 205 | -o, --output Output file [default: flamegraph.svg] 206 | --open Open the output .svg file with default program 207 | --root Run with root privileges (using `sudo`) 208 | -F, --freq Sampling frequency in Hz [default: 997] 209 | -c, --cmd Custom command for invoking perf/dtrace 210 | --deterministic Colors are selected such that the color of a function does not change between runs 211 | -i, --inverted Plot the flame graph up-side-down 212 | --reverse Generate stack-reversed flame graph 213 | --notes Set embedded notes in SVG 214 | --min-width Omit functions smaller than pixels [default: 0.01] 215 | --image-width Image width in pixels 216 | --palette Color palette [possible values: hot, mem, io, red, green, blue, aqua, yellow, purple, orange, wakeup, java, perl, js, rust] 217 | --skip-after Cut off stack frames below ; may be repeated 218 | --flamechart Produce a flame chart (sort by time, do not merge stacks) 219 | --ignore-status Ignores perf's exit code 220 | --no-inline Disable inlining for perf script because of performance issues 221 | --post-process Run a command to process the folded stacks, taking the input from stdin and outputting to stdout 222 | -h, --help Print help 223 | -V, --version Print version 224 | ``` 225 | 226 | Then open the resulting `flamegraph.svg` with a browser, because most image 227 | viewers do not support interactive svg-files. 228 | 229 | ## Enabling perf for use by unprivileged users 230 | 231 | To enable perf without running as root, you may 232 | lower the `perf_event_paranoid` value in proc 233 | to an appropriate level for your environment. 234 | The most permissive value is `-1` but may not 235 | be acceptable for your security needs etc... 236 | 237 | ```bash 238 | echo -1 | sudo tee /proc/sys/kernel/perf_event_paranoid 239 | ``` 240 | 241 | ## Improving output when running with `--release` 242 | 243 | Due to optimizations etc... sometimes the quality 244 | of the information presented in the flamegraph will 245 | suffer when profiling release builds. 246 | 247 | To counter this to some extent, you may either set the following in your 248 | `Cargo.toml` file: 249 | 250 | ``` 251 | [profile.release] 252 | debug = true 253 | ``` 254 | 255 | Or set the environment variable [CARGO_PROFILE_RELEASE_DEBUG=true](https://doc.rust-lang.org/cargo/reference/config.html#profilenamedebug). 256 | 257 | Please note that tests, unit tests and benchmarks use the `bench` profile in release mode (see [here](https://doc.rust-lang.org/cargo/reference/profiles.html#profile-selection)). 258 | 259 | ## Usage with benchmarks 260 | 261 | In order to perf existing benchmarks, you should set up a few configs. 262 | Set the following in your `Cargo.toml` file to run benchmarks: 263 | 264 | ``` 265 | [profile.bench] 266 | debug = true 267 | ``` 268 | 269 | ## Use custom paths for perf and dtrace 270 | 271 | If `PERF` or `DTRACE` environment variable is set, 272 | it'll be used as corresponding tool command. 273 | For example, to use `perf` from `~/bin`: 274 | 275 | ```bash 276 | env PERF=~/bin/perf flamegraph /path/to/my/binary 277 | ``` 278 | 279 | ## Use custom `addr2line` binary for perf 280 | 281 | It has been reported that `addr2line` can run very slowly in several issues ([#74][i74], [#199][i199], [#294][i294]). One solution is to use [gimli-rs/addr2line](https://github.com/gimli-rs/addr2line) instead of the system `addr2line` binary. This is suggested in [this comment](https://github.com/flamegraph-rs/flamegraph/issues/74#issuecomment-1909417039), and you can follow the steps below to set it up: 282 | 283 | [i74]: https://github.com/flamegraph-rs/flamegraph/issues/74 284 | [i199]: https://github.com/flamegraph-rs/flamegraph/issues/199 285 | [i294]: https://github.com/flamegraph-rs/flamegraph/issues/294 286 | 287 | ```bash 288 | cargo install addr2line --features=bin 289 | ``` 290 | 291 | # Systems Performance Work Guided By Flamegraphs 292 | 293 | Flamegraphs are used to visualize where time is being spent 294 | in your program. Many times per second, the threads in a 295 | program are interrupted and the current location in your 296 | code (based on the thread's instruction pointer) is recorded, 297 | along with the chain of functions that were called to get there. 298 | This is called stack sampling. These samples are then 299 | processed and stacks that share common functions are 300 | added together. Then an SVG is generated showing the 301 | call stacks that were measured, widened to the proportion 302 | of all stack samples that contained them. 303 | 304 | The **y-axis** shows the stack depth number. When looking at a 305 | flamegraph, the main function of your program will be closer to 306 | the bottom, and the called functions will be stacked on top, 307 | with the functions that they call stacked on top of them, etc... 308 | 309 | The **x-axis** spans all of the samples. It does _not_ show the 310 | passing of time from left to right. The left to right ordering 311 | has no meaning. 312 | 313 | The **width** of each box shows the total time that that 314 | function is on the CPU or is part of the call stack. If a 315 | function's box is wider than others, that means that it consumes 316 | more CPU per execution than other functions, or that it is 317 | called more than other functions. 318 | 319 | The **color** of each box isn't significant, and is chosen at 320 | random. 321 | 322 | Flamegraphs are good for visualizing where the most 323 | expensive parts of your program are at runtime, 324 | which is wonderful because... 325 | 326 | ## Humans are terrible at guessing about performance! 327 | 328 | Especially people who come to Rust from C and C++ will 329 | often over-optimize things in code that LLVM is able to 330 | optimize away on its own. It's always better to write 331 | Rust in a clear and obvious way, before beginning 332 | micro-optimizations, allocation-minimization, etc... 333 | 334 | Lots of things that would seem like they would have terrible 335 | performance are actually cheap or free in Rust. Closures 336 | are fast. Initialization on the stack before moving 337 | to a `Box` is often compiled away. Clones are often 338 | compiled away. So, `clone()` away instead of fighting 339 | for too long to get the compiler to be happy about 340 | ownership! 341 | 342 | Then make a flamegraph to see if any of that was 343 | actually expensive. 344 | 345 | ## Flamegraphs Are the Beginning, Not the End 346 | 347 | Flamegraphs show you the things that are taking up time, but they 348 | are a sampling technique to be used for high-level and initial 349 | looks at the system under measurement. They are great for finding 350 | the things to look into more closely, and often it will be 351 | obvious how to improve something based on its flamegraph, but 352 | they are really more for choosing the target to perform optimization 353 | on than an optimization measurement tool in itself. They are 354 | coarse-grained, and difficult to diff (although 355 | [this may be supported soon](https://github.com/jonhoo/inferno/issues/62)). 356 | Also, because flamegraphs are based on the proportion of total time 357 | that something takes, if you accidentally make something else 358 | really slow, it will show all other things as smaller on the flamegraph, 359 | even though the entire program runtime is much slower, the items you 360 | were hoping to optimize look smaller. 361 | 362 | It is a good idea to use Flamegraphs to figure out what you want to 363 | optimize, and then set up a measurement environment that allows 364 | you to determine that an improvement has actually happened. 365 | 366 | - use flamegraphs to find a set of optimization targets 367 | - create benchmarks for these optimization targets, and if 368 | appropriate use something like cachegrind and cg_diff to 369 | [measure cpu instructions](https://github.com/spacejam/sled/blob/d521c510c3b8a7e02b8602d6db6a7701b51bd33b/hack/instructions#L26) 370 | and diff them against the previous version. 371 | - Measuring CPU instructions is often better than measuring the time it takes 372 | to run a workload in many cases, because it's possible that a 373 | background task on your machine ran and caused something to slow down 374 | in terms of physical time, but if you actually made an implementation 375 | faster, it is likely to have a stronger correlation with reduced total 376 | CPU instructions. 377 | - Time spent on the CPU is not the full picture, as time is spent 378 | waiting for IO to complete as well, which does not get accounted 379 | with tools like perf that only measure what's consuming time 380 | on the CPU. Check out [Brendan Gregg's article on Off-Cpu 381 | Accounting](http://www.brendangregg.com/offcpuanalysis.html) 382 | for more information about this! 383 | 384 | ## Performance Theory 101: Basics of Quantitative Engineering 385 | 386 | - Use realistic workloads on realistic hardware, or your data doesn't 387 | necessarily correspond very much with what will be happening in production 388 | - All of our guesses are wrong to some extent, so we have to measure 389 | the effects of our work. Often the simple code that doesn't seem 390 | like it should be fast is actually way faster than code that looks 391 | optimized. We need to measure our optimizations to make sure that we 392 | didn't make our code both harder to read AND slower. 393 | - Measure before you change anything, and save the results 394 | in a safe place! Many profiling tools will overwrite their old output 395 | when you run them again, so make sure you take care to save the 396 | data before you begin so that you can compare before and after. 397 | - Take measurements on a warmed up machine that isn't doing anything 398 | else, and has had time to cool off from the last workload. 399 | CPUs will fall asleep and drop into power-saving modes when idle, 400 | and they will also throttle if they get too hot (sometimes SIMD 401 | can cause things to run slower because it heats things up so much 402 | that the core has to throttle). 403 | 404 | ## Performance Theory 202: USE Method 405 | 406 | The USE Method is a way to very quickly locate performance 407 | problems while minimizing discovery efforts. It's more about 408 | finding production issues than flamegraphs directly, but it's 409 | a great technique to have in your toolbox if you are going to 410 | be doing performance triage, and flamegraphs can be helpful 411 | for identifying the components to then drill down into queue 412 | analysis for. 413 | 414 | Everything in a computer can be thought of as a resource 415 | with a queue in front of it, which can serve one or more 416 | requests at a time. The various systems in our computers 417 | and programs can do a certain amount of work over time 418 | before requests start to pile up and wait in a line 419 | until they are able to be serviced. 420 | 421 | Some resources can handle more and more work without degrading 422 | in performance until they hit their maximum utilization point. 423 | Network devices can be thought of as working in this way to 424 | a large extent. Other resources start to saturate long before 425 | they hit their maximum utilization point, like disks. 426 | 427 | Disks (especially spinning disks, but even SSDs) will do more and 428 | more work if you allow more work to queue up until they hit their 429 | maximum throughput for a workload, but the latency per request 430 | will go up before it hits 100% utilization because the disk will 431 | take longer before it can begin servicing each request. Tuning disk 432 | performance often involves measuring the various IO queue depths to 433 | make sure they are high enough to get nice throughput but not so 434 | high that latency becomes undesirable. 435 | 436 | Anyway, nearly everything in our systems can be broken down 437 | to be analyzed based on 3 high-level characteristics: 438 | 439 | - **Utilization** is the amount of time the system under 440 | measurement is actually doing useful work servicing a request, 441 | and can be measured as the percent of available time spent servicing 442 | requests 443 | - **Saturation** is when requests have to wait before being 444 | serviced. This can be measured as the queue depth over time 445 | - **Errors** are when things start to fail, like when queues 446 | are no longer able to accept any new requests - like when a TCP connection 447 | is rejected because the system's TCP backlog is already full of 448 | connections that have not yet been accept'ed by the userspace 449 | program. 450 | 451 | This forms the necessary background to start applying the USE Method 452 | to locate the performance-related issue in your complex system! 453 | 454 | The approach is: 455 | 456 | 1. Enumerate the various resources that might be behaving poorly - maybe by creating a 457 | flamegraph and looking for functions that are taking more of the total runtime than expected 458 | 1. Pick one of them 459 | 1. (Errors) Check for errors like TCP connection failures, other IO failures, bad things in logs etc... 460 | 1. (Utilization) Measure the utilization of the system and see if its throughput is approaching 461 | the known maximum, or the point that it is known to experience saturation 462 | 1. (Saturation) Is saturation actually happening? Are requests waiting in lines before being serviced? 463 | Is latency going up while throughput is staying the same? 464 | 465 | These probing questions serve as a piercing flashlight for 466 | rapidly identifying the underlying issue most of the time. 467 | 468 | If you want to learn more about this, check out Brendan Gregg's 469 | [blog post](http://www.brendangregg.com/usemethod.html) on it. 470 | I tend to recommend that anyone who is becoming an SRE should 471 | make Brendan's 472 | [Systems Performance](http://www.brendangregg.com/sysperfbook.html) 473 | book one of the first things they read to understand how to 474 | measure these things quickly in production systems. 475 | 476 | The USE Method derives from an area of study called 477 | [queue theory](https://en.wikipedia.org/wiki/Queueing_theory) 478 | which has had a huge impact on the world of computing, 479 | as well as many other logistical endeavors that humans 480 | have undertaken. 481 | 482 | ## Performance Laws 483 | 484 | If you want to drill more into theory, know the law(s)! 485 | 486 | - [Universal Law of Scalability](http://www.perfdynamics.com/Manifesto/USLscalability.html) 487 | is about the relationship between concurrency gains, queuing and coordination costs 488 | - [Amdahl's Law](https://en.wikipedia.org/wiki/Amdahl%27s_law) 489 | is about the theoretical maximum gain that can be made for a workload by parallelization. 490 | - [Little's Law](https://en.wikipedia.org/wiki/Little%27s_law) 491 | is a deceptively simple law with some subtle implications from queue theory 492 | that allows us to reason about appropriate queue lengths for our systems 493 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | env, 3 | fs::File, 4 | io::{BufReader, BufWriter, Cursor, Read, Write}, 5 | path::PathBuf, 6 | process::{exit, Command, ExitStatus, Stdio}, 7 | str::FromStr, 8 | }; 9 | 10 | #[cfg(unix)] 11 | use std::os::unix::process::ExitStatusExt; 12 | 13 | #[cfg(target_os = "linux")] 14 | use inferno::collapse::perf::{Folder, Options as CollapseOptions}; 15 | 16 | #[cfg(target_os = "macos")] 17 | use inferno::collapse::xctrace::Folder; 18 | 19 | #[cfg(not(any(target_os = "linux", target_os = "macos")))] 20 | use inferno::collapse::dtrace::{Folder, Options as CollapseOptions}; 21 | 22 | #[cfg(unix)] 23 | use signal_hook::consts::{SIGINT, SIGTERM}; 24 | 25 | use anyhow::{anyhow, bail, Context}; 26 | use clap::{ 27 | builder::{PossibleValuesParser, TypedValueParser}, 28 | Args, 29 | }; 30 | use inferno::{collapse::Collapse, flamegraph::color::Palette, flamegraph::from_reader}; 31 | use rustc_demangle::demangle_stream; 32 | 33 | pub enum Workload { 34 | Command(Vec), 35 | Pid(Vec), 36 | ReadPerf(PathBuf), 37 | } 38 | 39 | #[cfg(target_os = "linux")] 40 | mod arch { 41 | use std::fmt::Write; 42 | use std::time::Duration; 43 | 44 | use indicatif::{ProgressBar, ProgressStyle}; 45 | 46 | use super::*; 47 | 48 | pub const SPAWN_ERROR: &str = "could not spawn perf"; 49 | pub const WAIT_ERROR: &str = "unable to wait for perf child command to exit"; 50 | 51 | pub(crate) fn initial_command( 52 | workload: Workload, 53 | sudo: Option>, 54 | freq: u32, 55 | compress_level: Option, 56 | custom_cmd: Option, 57 | verbose: bool, 58 | ignore_status: bool, 59 | ) -> anyhow::Result> { 60 | let perf = if let Ok(path) = env::var("PERF") { 61 | path 62 | } else { 63 | if Command::new("perf") 64 | .arg("--help") 65 | .stderr(Stdio::null()) 66 | .stdout(Stdio::null()) 67 | .status() 68 | .is_err() 69 | { 70 | bail!("perf is not installed or not present in $PATH"); 71 | } 72 | 73 | String::from("perf") 74 | }; 75 | let mut command = sudo_command(&perf, sudo); 76 | 77 | let args = custom_cmd.unwrap_or_else(|| { 78 | let mut args = format!("record -F {freq} --call-graph dwarf,64000 -g"); 79 | if let Some(z) = compress_level { 80 | _ = write!(args, " -z={z}"); 81 | } 82 | args 83 | }); 84 | 85 | let mut perf_output = None; 86 | let mut args = args.split_whitespace(); 87 | while let Some(arg) = args.next() { 88 | command.arg(arg); 89 | 90 | // Detect if user is setting `perf record` 91 | // output file with `-o`. If so, save it in 92 | // order to correctly compute perf's output in 93 | // `Self::output`. 94 | if arg == "-o" { 95 | let next_arg = args.next().context("missing '-o' argument")?; 96 | command.arg(next_arg); 97 | perf_output = Some(PathBuf::from(next_arg)); 98 | } 99 | } 100 | 101 | let perf_output = match perf_output { 102 | Some(path) => path, 103 | None => { 104 | command.arg("-o"); 105 | command.arg("perf.data"); 106 | PathBuf::from("perf.data") 107 | } 108 | }; 109 | 110 | match workload { 111 | Workload::Command(c) => { 112 | command.args(&c); 113 | } 114 | Workload::Pid(p) => { 115 | if let Some((first, pids)) = p.split_first() { 116 | let mut arg = first.to_string(); 117 | 118 | for pid in pids { 119 | arg.push(','); 120 | arg.push_str(&pid.to_string()); 121 | } 122 | 123 | command.arg("-p"); 124 | command.arg(arg); 125 | } 126 | } 127 | Workload::ReadPerf(_) => (), 128 | } 129 | 130 | run(command, verbose, ignore_status); 131 | Ok(Some(perf_output)) 132 | } 133 | 134 | pub fn output( 135 | perf_output: Option, 136 | script_no_inline: bool, 137 | sudo: Option>, 138 | ) -> anyhow::Result> { 139 | // We executed `perf record` with sudo, and will be executing `perf script` with sudo, 140 | // so that we can resolve privileged kernel symbols from /proc/kallsyms. 141 | let perf = env::var("PERF").unwrap_or_else(|_| "perf".to_string()); 142 | let mut command = sudo_command(&perf, sudo); 143 | 144 | command.arg("script"); 145 | 146 | // Force reading perf.data owned by another uid if it happened to be created earlier. 147 | command.arg("--force"); 148 | 149 | if script_no_inline { 150 | command.arg("--no-inline"); 151 | } 152 | 153 | if let Some(perf_output) = perf_output { 154 | command.arg("-i"); 155 | command.arg(perf_output); 156 | } 157 | 158 | // perf script can take a long time to run. Notify the user that it is running 159 | // by using a spinner. Note that if this function exits before calling 160 | // spinner.finish(), then the spinner will be completely removed from the terminal. 161 | let spinner = ProgressBar::new_spinner().with_prefix("Running perf script"); 162 | spinner.set_style( 163 | ProgressStyle::with_template("{prefix} [{elapsed}]: {spinner:.green}").unwrap(), 164 | ); 165 | spinner.enable_steady_tick(Duration::from_millis(500)); 166 | 167 | let result = command.output().context("unable to call perf script"); 168 | spinner.finish(); 169 | let output = result?; 170 | if !output.status.success() { 171 | bail!( 172 | "unable to run 'perf script': ({}) {}", 173 | output.status, 174 | std::str::from_utf8(&output.stderr)? 175 | ); 176 | } 177 | Ok(output.stdout) 178 | } 179 | } 180 | 181 | #[cfg(target_os = "macos")] 182 | mod arch { 183 | use super::*; 184 | 185 | pub const SPAWN_ERROR: &str = "could not spawn xctrace"; 186 | pub const WAIT_ERROR: &str = "unable to wait for xctrace record child command to exit"; 187 | 188 | pub(crate) fn initial_command( 189 | workload: Workload, 190 | sudo: Option>, 191 | freq: u32, 192 | _compress_level: Option, 193 | custom_cmd: Option, 194 | verbose: bool, 195 | ignore_status: bool, 196 | ) -> anyhow::Result> { 197 | if freq != 997 { 198 | bail!("xctrace doesn't support custom frequency"); 199 | } 200 | if custom_cmd.is_some() { 201 | bail!("xctrace doesn't support custom command"); 202 | } 203 | let xctrace = env::var("XCTRACE").unwrap_or_else(|_| "xctrace".to_string()); 204 | let trace_file = PathBuf::from("cargo-flamegraph.trace"); 205 | let mut command = sudo_command(&xctrace, sudo); 206 | command 207 | .arg("record") 208 | .arg("--template") 209 | .arg("Time Profiler") 210 | .arg("--output") 211 | .arg(&trace_file); 212 | match workload { 213 | Workload::Command(args) => { 214 | command 215 | .arg("--target-stdout") 216 | .arg("-") 217 | .arg("--launch") 218 | .arg("--") 219 | .args(args); 220 | } 221 | Workload::Pid(pid) => { 222 | match &*pid { 223 | [pid] => { 224 | // xctrace could accept multiple --attach arguments, 225 | // but it will only profile the last pid provided. 226 | command.arg("--attach").arg(pid.to_string()); 227 | } 228 | _ => { 229 | bail!("xctrace only supports profiling a single process at a time"); 230 | } 231 | } 232 | } 233 | Workload::ReadPerf(_) => {} 234 | } 235 | run(command, verbose, ignore_status); 236 | Ok(Some(trace_file)) 237 | } 238 | 239 | pub fn output( 240 | trace_file: Option, 241 | script_no_inline: bool, 242 | _sudo: Option>, 243 | ) -> anyhow::Result> { 244 | if script_no_inline { 245 | bail!("--no-inline is only supported on Linux"); 246 | } 247 | 248 | let xctrace = env::var("XCTRACE").unwrap_or_else(|_| "xctrace".to_string()); 249 | let trace_file = trace_file.context("no trace file found.")?; 250 | let output = Command::new(xctrace) 251 | .arg("export") 252 | .arg("--input") 253 | .arg(&trace_file) 254 | .arg("--xpath") 255 | .arg(r#"/trace-toc/*/data/table[@schema="time-profile"]"#) 256 | .output() 257 | .context("run xctrace export failed.")?; 258 | std::fs::remove_dir_all(&trace_file) 259 | .with_context(|| anyhow!("remove trace({}) failed.", trace_file.to_string_lossy()))?; 260 | if !output.status.success() { 261 | bail!( 262 | "unable to run 'xctrace export': ({}) {}", 263 | output.status, 264 | String::from_utf8_lossy(&output.stderr) 265 | ); 266 | } 267 | Ok(output.stdout) 268 | } 269 | } 270 | 271 | #[cfg(not(any(target_os = "linux", target_os = "macos")))] 272 | mod arch { 273 | use super::*; 274 | 275 | pub const SPAWN_ERROR: &str = "could not spawn dtrace"; 276 | pub const WAIT_ERROR: &str = "unable to wait for dtrace child command to exit"; 277 | 278 | #[cfg(target_os = "macos")] 279 | fn base_dtrace_command(sudo: Option>) -> Command { 280 | // If DTrace is spawned from a parent process (or grandparent process etc.) running in Rosetta-emulated x86 mode 281 | // on an ARM mac, it will fail to trace the child process with a confusing syntax error in its stdlib .d file. 282 | // If the flamegraph binary, or the cargo binary, have been compiled as x86, this can cause all tracing to fail. 283 | // To work around that, we unconditionally wrap dtrace on MacOS in the "arch -64/-32" wrapper so it's always 284 | // running in the native architecture matching the bit width (32 oe 64) with which "flamegraph" was compiled. 285 | // NOTE that dtrace-as-x86 won't trace a deliberately-cross-compiled x86 binary running under Rosetta regardless 286 | // of "arch" wrapping; attempts to do that will fail with "DTrace cannot instrument translated processes". 287 | // NOTE that using the ARCHPREFERENCE environment variable documented here 288 | // (https://www.unix.com/man-page/osx/1/arch/) would be a much simpler solution to this issue, but it does not 289 | // seem to have any effect on dtrace when set (via Command::env, shell export, or std::env in the spawning 290 | // process). 291 | let mut command = sudo_command("arch", sudo); 292 | 293 | #[cfg(target_pointer_width = "64")] 294 | command.arg("-64".to_string()); 295 | #[cfg(target_pointer_width = "32")] 296 | command.arg("-32".to_string()); 297 | 298 | command.arg(env::var("DTRACE").unwrap_or_else(|_| "dtrace".to_string())); 299 | command 300 | } 301 | 302 | #[cfg(not(target_os = "macos"))] 303 | fn base_dtrace_command(sudo: Option>) -> Command { 304 | let dtrace = env::var("DTRACE").unwrap_or_else(|_| "dtrace".to_string()); 305 | sudo_command(&dtrace, sudo) 306 | } 307 | 308 | pub(crate) fn initial_command( 309 | workload: Workload, 310 | sudo: Option>, 311 | freq: u32, 312 | _compress_level: Option, 313 | custom_cmd: Option, 314 | verbose: bool, 315 | ignore_status: bool, 316 | ) -> anyhow::Result> { 317 | let mut command = base_dtrace_command(sudo); 318 | 319 | let dtrace_script = custom_cmd.unwrap_or(format!( 320 | "profile-{freq} /pid == $target/ \ 321 | {{ @[ustack(100)] = count(); }}", 322 | )); 323 | 324 | command.arg("-x"); 325 | command.arg("ustackframes=100"); 326 | 327 | command.arg("-n"); 328 | command.arg(&dtrace_script); 329 | 330 | command.arg("-o"); 331 | command.arg("cargo-flamegraph.stacks"); 332 | 333 | match workload { 334 | Workload::Command(c) => { 335 | let mut escaped = String::new(); 336 | for (i, arg) in c.iter().enumerate() { 337 | if i > 0 { 338 | escaped.push(' '); 339 | } 340 | escaped.push_str(&arg.replace(' ', "\\ ")); 341 | } 342 | 343 | command.arg("-c"); 344 | command.arg(&escaped); 345 | 346 | #[cfg(target_os = "windows")] 347 | { 348 | let mut help_test = crate::arch::base_dtrace_command(None); 349 | 350 | let dtrace_found = help_test 351 | .arg("--help") 352 | .stderr(Stdio::null()) 353 | .stdout(Stdio::null()) 354 | .status() 355 | .is_ok(); 356 | if !dtrace_found { 357 | let mut command_builder = Command::new(&c[0]); 358 | command_builder.args(&c[1..]); 359 | print_command(&command_builder, verbose); 360 | 361 | let trace = blondie::trace_command(command_builder, false) 362 | .map_err(|err| anyhow!("could not find dtrace and could not profile using blondie: {err:?}"))?; 363 | 364 | let f = std::fs::File::create("./cargo-flamegraph.stacks") 365 | .context("unable to create temporary file 'cargo-flamegraph.stacks'")?; 366 | let mut f = std::io::BufWriter::new(f); 367 | trace.write_dtrace(&mut f).map_err(|err| { 368 | anyhow!("unable to write dtrace output to 'cargo-flamegraph.stacks': {err:?}") 369 | })?; 370 | 371 | return Ok(None); 372 | } 373 | } 374 | } 375 | Workload::Pid(p) => { 376 | for p in p { 377 | command.arg("-p"); 378 | command.arg(p.to_string()); 379 | } 380 | } 381 | Workload::ReadPerf(_) => (), 382 | } 383 | 384 | run(command, verbose, ignore_status); 385 | Ok(None) 386 | } 387 | 388 | pub fn output( 389 | _: Option, 390 | script_no_inline: bool, 391 | sudo: Option>, 392 | ) -> anyhow::Result> { 393 | if script_no_inline { 394 | bail!("--no-inline is only supported on Linux"); 395 | } 396 | 397 | // Ensure the file is readable by the current user if dtrace was run 398 | // with sudo. 399 | if sudo.is_some() { 400 | #[cfg(unix)] 401 | if let Ok(user) = env::var("USER") { 402 | Command::new("sudo") 403 | .args(["chown", user.as_str(), "cargo-flamegraph.stacks"]) 404 | .spawn() 405 | .expect(arch::SPAWN_ERROR) 406 | .wait() 407 | .expect(arch::WAIT_ERROR); 408 | } 409 | } 410 | 411 | let mut buf = vec![]; 412 | let mut f = File::open("cargo-flamegraph.stacks") 413 | .context("failed to open dtrace output file 'cargo-flamegraph.stacks'")?; 414 | 415 | f.read_to_end(&mut buf) 416 | .context("failed to read dtrace expected output file 'cargo-flamegraph.stacks'")?; 417 | 418 | std::fs::remove_file("cargo-flamegraph.stacks") 419 | .context("unable to remove temporary file 'cargo-flamegraph.stacks'")?; 420 | 421 | // Workaround #32 - fails parsing invalid utf8 dtrace output 422 | // 423 | // Intermittently, invalid utf-8 is found in cargo-flamegraph.stacks, which 424 | // causes parsing to blow up with the error: 425 | // 426 | // > unable to collapse generated profile data: Custom { kind: InvalidData, error: StringError("stream did not contain valid UTF-8") } 427 | // 428 | // So here we just lossily re-encode to hopefully work around the underlying problem 429 | let string = String::from_utf8_lossy(&buf); 430 | let reencoded_buf = string.as_bytes().to_owned(); 431 | 432 | if reencoded_buf != buf { 433 | println!("Lossily converted invalid utf-8 found in cargo-flamegraph.stacks"); 434 | } 435 | 436 | Ok(reencoded_buf) 437 | } 438 | } 439 | 440 | fn sudo_command(command: &str, sudo: Option>) -> Command { 441 | let sudo = match sudo { 442 | Some(sudo) => sudo, 443 | None => return Command::new(command), 444 | }; 445 | 446 | let mut c = Command::new("sudo"); 447 | if let Some(sudo_args) = sudo { 448 | c.arg(sudo_args); 449 | } 450 | c.arg(command); 451 | c 452 | } 453 | 454 | fn run(mut command: Command, verbose: bool, ignore_status: bool) { 455 | print_command(&command, verbose); 456 | let mut recorder = command.spawn().expect(arch::SPAWN_ERROR); 457 | let exit_status = recorder.wait().expect(arch::WAIT_ERROR); 458 | 459 | // only stop if perf exited unsuccessfully, but 460 | // was not killed by a signal (assuming that the 461 | // latter case usually means the user interrupted 462 | // it in some way) 463 | if !ignore_status && terminated_by_error(exit_status) { 464 | eprintln!( 465 | "failed to sample program, exited with code: {:?}", 466 | exit_status.code() 467 | ); 468 | exit(1); 469 | } 470 | } 471 | 472 | #[cfg(unix)] 473 | fn terminated_by_error(status: ExitStatus) -> bool { 474 | status 475 | .signal() // the default needs to be true because that's the neutral element for `&&` 476 | .map_or(true, |code| code != SIGINT && code != SIGTERM) 477 | && !status.success() 478 | // on macOS, xctrace captures Ctrl+C and exits with code 54 479 | && !(cfg!(target_os = "macos") && status.code() == Some(54)) 480 | } 481 | 482 | #[cfg(not(unix))] 483 | fn terminated_by_error(status: ExitStatus) -> bool { 484 | !status.success() 485 | } 486 | 487 | fn print_command(cmd: &Command, verbose: bool) { 488 | if verbose { 489 | println!("command {:?}", cmd); 490 | } 491 | } 492 | 493 | pub fn generate_flamegraph_for_workload(workload: Workload, opts: Options) -> anyhow::Result<()> { 494 | // Handle SIGINT with an empty handler. This has the 495 | // implicit effect of allowing the signal to reach the 496 | // process under observation while we continue to 497 | // generate our flamegraph. (ctrl+c will send the 498 | // SIGINT signal to all processes in the foreground 499 | // process group). 500 | #[cfg(unix)] 501 | let handler = unsafe { 502 | signal_hook::low_level::register(SIGINT, || {}).expect("cannot register signal handler") 503 | }; 504 | 505 | let sudo = opts.root.as_ref().map(|inner| inner.as_deref()); 506 | 507 | let perf_output = if let Workload::ReadPerf(perf_file) = workload { 508 | Some(perf_file) 509 | } else { 510 | #[cfg(target_os = "linux")] 511 | let compression = opts.compression_level; 512 | #[cfg(not(target_os = "linux"))] 513 | let compression = None; 514 | 515 | arch::initial_command( 516 | workload, 517 | sudo, 518 | opts.frequency(), 519 | compression, 520 | opts.custom_cmd, 521 | opts.verbose, 522 | opts.ignore_status, 523 | )? 524 | }; 525 | 526 | #[cfg(unix)] 527 | signal_hook::low_level::unregister(handler); 528 | 529 | let output = arch::output(perf_output, opts.script_no_inline, sudo)?; 530 | 531 | let mut demangled_output = vec![]; 532 | 533 | demangle_stream(&mut Cursor::new(output), &mut demangled_output, false) 534 | .context("unable to demangle")?; 535 | 536 | let perf_reader = BufReader::new(&*demangled_output); 537 | 538 | let mut collapsed = vec![]; 539 | 540 | let collapsed_writer = BufWriter::new(&mut collapsed); 541 | 542 | #[cfg(target_os = "linux")] 543 | let mut folder = { 544 | let mut collapse_options = CollapseOptions::default(); 545 | collapse_options.skip_after = opts.flamegraph_options.skip_after.clone(); 546 | Folder::from(collapse_options) 547 | }; 548 | 549 | #[cfg(target_os = "macos")] 550 | let mut folder = Folder::default(); 551 | 552 | #[cfg(not(any(target_os = "linux", target_os = "macos")))] 553 | let mut folder = { 554 | let collapse_options = CollapseOptions::default(); 555 | Folder::from(collapse_options) 556 | }; 557 | 558 | folder 559 | .collapse(perf_reader, collapsed_writer) 560 | .context("unable to collapse generated profile data")?; 561 | 562 | if let Some(command) = opts.post_process { 563 | let command_vec = shlex::split(&command) 564 | .ok_or_else(|| anyhow!("unable to parse post-process command"))?; 565 | 566 | let mut child = Command::new( 567 | command_vec 568 | .first() 569 | .ok_or_else(|| anyhow!("unable to parse post-process command"))?, 570 | ) 571 | .args(command_vec.get(1..).unwrap_or(&[])) 572 | .stdin(Stdio::piped()) 573 | .stdout(Stdio::piped()) 574 | .spawn() 575 | .with_context(|| format!("unable to execute {:?}", command_vec))?; 576 | 577 | let mut stdin = child 578 | .stdin 579 | .take() 580 | .ok_or_else(|| anyhow::anyhow!("unable to capture post-process stdin"))?; 581 | 582 | let mut stdout = child 583 | .stdout 584 | .take() 585 | .ok_or_else(|| anyhow::anyhow!("unable to capture post-process stdout"))?; 586 | 587 | let thread_handle = std::thread::spawn(move || -> anyhow::Result<_> { 588 | let mut collapsed_processed = Vec::new(); 589 | stdout.read_to_end(&mut collapsed_processed).context( 590 | "unable to read the processed stacks from the stdout of the post-process process", 591 | )?; 592 | Ok(collapsed_processed) 593 | }); 594 | 595 | stdin 596 | .write_all(&collapsed) 597 | .context("unable to write the raw stacks to the stdin of the post-process process")?; 598 | drop(stdin); 599 | 600 | anyhow::ensure!( 601 | child.wait()?.success(), 602 | "post-process exited with a non zero exit code" 603 | ); 604 | 605 | collapsed = thread_handle.join().unwrap()?; 606 | } 607 | 608 | let collapsed_reader = BufReader::new(&*collapsed); 609 | 610 | let flamegraph_filename = opts.output; 611 | println!("writing flamegraph to {:?}", flamegraph_filename); 612 | let flamegraph_file = File::create(&flamegraph_filename) 613 | .context("unable to create flamegraph.svg output file")?; 614 | 615 | let flamegraph_writer = BufWriter::new(flamegraph_file); 616 | 617 | let mut inferno_opts = opts.flamegraph_options.into_inferno(); 618 | from_reader(&mut inferno_opts, collapsed_reader, flamegraph_writer) 619 | .context("unable to generate a flamegraph from the collapsed stack data")?; 620 | 621 | if opts.open { 622 | opener::open(&flamegraph_filename).context(format!( 623 | "failed to open '{}'", 624 | flamegraph_filename.display() 625 | ))?; 626 | } 627 | 628 | Ok(()) 629 | } 630 | 631 | #[derive(Debug, Args)] 632 | pub struct Options { 633 | /// Print extra output to help debug problems 634 | #[clap(short, long)] 635 | pub verbose: bool, 636 | 637 | /// Output file 638 | #[clap(short, long, default_value = "flamegraph.svg")] 639 | output: PathBuf, 640 | 641 | /// Open the output .svg file with default program 642 | #[clap(long)] 643 | open: bool, 644 | 645 | /// Run with root privileges (using `sudo`). Accepts an optional argument containing command line options which will be passed to sudo 646 | #[clap(long, value_name = "SUDO FLAGS")] 647 | pub root: Option>, 648 | 649 | /// Sampling frequency in Hz [default: 997] 650 | #[clap(short = 'F', long = "freq")] 651 | frequency: Option, 652 | 653 | /// Produce compressed trace using the specified level. perf only. 654 | #[cfg(target_os = "linux")] 655 | #[clap(short = 'z', long, num_args=0..=1, default_missing_value = "1")] 656 | compression_level: Option, 657 | 658 | /// Custom command for invoking perf/dtrace 659 | #[clap(short, long = "cmd")] 660 | custom_cmd: Option, 661 | 662 | #[clap(flatten)] 663 | flamegraph_options: FlamegraphOptions, 664 | 665 | /// Ignores perf's exit code 666 | #[clap(long)] 667 | ignore_status: bool, 668 | 669 | /// Disable inlining for perf script because of performance issues 670 | #[clap(long = "no-inline")] 671 | script_no_inline: bool, 672 | 673 | /// Run a command to process the folded stacks, taking the input from stdin and outputting to 674 | /// stdout. 675 | #[clap(long)] 676 | post_process: Option, 677 | } 678 | 679 | impl Options { 680 | pub fn check(&self) -> anyhow::Result<()> { 681 | // Manually checking conflict because structopts `conflicts_with` leads 682 | // to a panic in completion generation for zsh at the moment (see #158) 683 | match self.frequency.is_some() && self.custom_cmd.is_some() { 684 | true => Err(anyhow!( 685 | "Cannot pass both a custom command and a frequency." 686 | )), 687 | false => Ok(()), 688 | } 689 | } 690 | 691 | pub fn frequency(&self) -> u32 { 692 | self.frequency.unwrap_or(997) 693 | } 694 | } 695 | 696 | #[derive(Debug, Args)] 697 | pub struct FlamegraphOptions { 698 | /// Set title text in SVG 699 | #[clap(long, value_name = "STRING")] 700 | pub title: Option, 701 | 702 | /// Set second level title text in SVG 703 | #[clap(long, value_name = "STRING")] 704 | pub subtitle: Option, 705 | 706 | /// Colors are selected such that the color of a function does not change between runs 707 | #[clap(long)] 708 | pub deterministic: bool, 709 | 710 | /// Plot the flame graph up-side-down 711 | #[clap(short, long)] 712 | pub inverted: bool, 713 | 714 | /// Generate stack-reversed flame graph 715 | #[clap(long)] 716 | pub reverse: bool, 717 | 718 | /// Set embedded notes in SVG 719 | #[clap(long, value_name = "STRING")] 720 | pub notes: Option, 721 | 722 | /// Omit functions smaller than pixels 723 | #[clap(long, default_value = "0.01", value_name = "FLOAT")] 724 | pub min_width: f64, 725 | 726 | /// Image width in pixels 727 | #[clap(long)] 728 | pub image_width: Option, 729 | 730 | /// Color palette 731 | #[clap( 732 | long, 733 | value_parser = PossibleValuesParser::new(Palette::VARIANTS).map(|s| Palette::from_str(&s).unwrap()) 734 | )] 735 | pub palette: Option, 736 | 737 | /// Cut off stack frames below ; may be repeated 738 | #[cfg(target_os = "linux")] 739 | #[clap(long, value_name = "FUNCTION")] 740 | pub skip_after: Vec, 741 | 742 | /// Produce a flame chart (sort by time, do not merge stacks) 743 | #[clap(long = "flamechart", conflicts_with = "reverse")] 744 | pub flame_chart: bool, 745 | } 746 | 747 | impl FlamegraphOptions { 748 | pub fn into_inferno(self) -> inferno::flamegraph::Options<'static> { 749 | let mut options = inferno::flamegraph::Options::default(); 750 | if let Some(title) = self.title { 751 | options.title = title; 752 | } 753 | options.subtitle = self.subtitle; 754 | options.deterministic = self.deterministic; 755 | if self.inverted { 756 | options.direction = inferno::flamegraph::Direction::Inverted; 757 | } 758 | options.reverse_stack_order = self.reverse; 759 | options.notes = self.notes.unwrap_or_default(); 760 | options.min_width = self.min_width; 761 | options.image_width = self.image_width; 762 | if let Some(palette) = self.palette { 763 | options.colors = palette; 764 | } 765 | options.flame_chart = self.flame_chart; 766 | 767 | options 768 | } 769 | } 770 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.24.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler2" 16 | version = "2.0.1" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" 19 | 20 | [[package]] 21 | name = "ahash" 22 | version = "0.8.12" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" 25 | dependencies = [ 26 | "cfg-if", 27 | "getrandom 0.3.3", 28 | "once_cell", 29 | "version_check", 30 | "zerocopy", 31 | ] 32 | 33 | [[package]] 34 | name = "alloc-no-stdlib" 35 | version = "2.0.4" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" 38 | 39 | [[package]] 40 | name = "alloc-stdlib" 41 | version = "0.2.2" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" 44 | dependencies = [ 45 | "alloc-no-stdlib", 46 | ] 47 | 48 | [[package]] 49 | name = "anstream" 50 | version = "0.6.20" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" 53 | dependencies = [ 54 | "anstyle", 55 | "anstyle-parse", 56 | "anstyle-query", 57 | "anstyle-wincon", 58 | "colorchoice", 59 | "is_terminal_polyfill", 60 | "utf8parse", 61 | ] 62 | 63 | [[package]] 64 | name = "anstyle" 65 | version = "1.0.11" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" 68 | 69 | [[package]] 70 | name = "anstyle-parse" 71 | version = "0.2.7" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" 74 | dependencies = [ 75 | "utf8parse", 76 | ] 77 | 78 | [[package]] 79 | name = "anstyle-query" 80 | version = "1.1.4" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" 83 | dependencies = [ 84 | "windows-sys 0.60.2", 85 | ] 86 | 87 | [[package]] 88 | name = "anstyle-wincon" 89 | version = "3.0.10" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" 92 | dependencies = [ 93 | "anstyle", 94 | "once_cell_polyfill", 95 | "windows-sys 0.60.2", 96 | ] 97 | 98 | [[package]] 99 | name = "anyhow" 100 | version = "1.0.99" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" 103 | 104 | [[package]] 105 | name = "arrayvec" 106 | version = "0.7.6" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" 109 | 110 | [[package]] 111 | name = "async-compression" 112 | version = "0.4.27" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "ddb939d66e4ae03cee6091612804ba446b12878410cfa17f785f4dd67d4014e8" 115 | dependencies = [ 116 | "brotli", 117 | "flate2", 118 | "futures-core", 119 | "memchr", 120 | "pin-project-lite", 121 | "tokio", 122 | ] 123 | 124 | [[package]] 125 | name = "autocfg" 126 | version = "1.5.0" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" 129 | 130 | [[package]] 131 | name = "backtrace" 132 | version = "0.3.75" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" 135 | dependencies = [ 136 | "addr2line", 137 | "cfg-if", 138 | "libc", 139 | "miniz_oxide", 140 | "object 0.36.7", 141 | "rustc-demangle", 142 | "windows-targets 0.52.6", 143 | ] 144 | 145 | [[package]] 146 | name = "base64" 147 | version = "0.21.7" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" 150 | 151 | [[package]] 152 | name = "binary-merge" 153 | version = "0.1.2" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "597bb81c80a54b6a4381b23faba8d7774b144c94cbd1d6fe3f1329bd776554ab" 156 | 157 | [[package]] 158 | name = "bitflags" 159 | version = "1.3.2" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 162 | 163 | [[package]] 164 | name = "bitflags" 165 | version = "2.9.2" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | checksum = "6a65b545ab31d687cff52899d4890855fec459eb6afe0da6417b8a18da87aa29" 168 | 169 | [[package]] 170 | name = "blondie" 171 | version = "0.5.2" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "15ebe57841bd1d5c836041c2b0a18c523dc6a5d9c85a9bb44bdccd091912649c" 174 | dependencies = [ 175 | "object 0.30.4", 176 | "pdb-addr2line", 177 | "rustc-hash", 178 | "symsrv", 179 | "tokio", 180 | "windows", 181 | ] 182 | 183 | [[package]] 184 | name = "brotli" 185 | version = "8.0.2" 186 | source = "registry+https://github.com/rust-lang/crates.io-index" 187 | checksum = "4bd8b9603c7aa97359dbd97ecf258968c95f3adddd6db2f7e7a5bef101c84560" 188 | dependencies = [ 189 | "alloc-no-stdlib", 190 | "alloc-stdlib", 191 | "brotli-decompressor", 192 | ] 193 | 194 | [[package]] 195 | name = "brotli-decompressor" 196 | version = "5.0.0" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" 199 | dependencies = [ 200 | "alloc-no-stdlib", 201 | "alloc-stdlib", 202 | ] 203 | 204 | [[package]] 205 | name = "bstr" 206 | version = "1.12.0" 207 | source = "registry+https://github.com/rust-lang/crates.io-index" 208 | checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" 209 | dependencies = [ 210 | "memchr", 211 | "regex-automata", 212 | "serde", 213 | ] 214 | 215 | [[package]] 216 | name = "bumpalo" 217 | version = "3.19.0" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" 220 | 221 | [[package]] 222 | name = "bytemuck" 223 | version = "1.23.2" 224 | source = "registry+https://github.com/rust-lang/crates.io-index" 225 | checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677" 226 | 227 | [[package]] 228 | name = "byteorder" 229 | version = "1.5.0" 230 | source = "registry+https://github.com/rust-lang/crates.io-index" 231 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 232 | 233 | [[package]] 234 | name = "bytes" 235 | version = "1.10.1" 236 | source = "registry+https://github.com/rust-lang/crates.io-index" 237 | checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" 238 | 239 | [[package]] 240 | name = "cab" 241 | version = "0.4.1" 242 | source = "registry+https://github.com/rust-lang/crates.io-index" 243 | checksum = "ae6b4de23c7d39c0631fd3cc952d87951c86c75a13812d7247cb7a896e7b3551" 244 | dependencies = [ 245 | "byteorder", 246 | "flate2", 247 | "lzxd", 248 | "time", 249 | ] 250 | 251 | [[package]] 252 | name = "camino" 253 | version = "1.1.11" 254 | source = "registry+https://github.com/rust-lang/crates.io-index" 255 | checksum = "5d07aa9a93b00c76f71bc35d598bed923f6d4f3a9ca5c24b7737ae1a292841c0" 256 | dependencies = [ 257 | "serde", 258 | ] 259 | 260 | [[package]] 261 | name = "cargo-platform" 262 | version = "0.3.0" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "8abf5d501fd757c2d2ee78d0cc40f606e92e3a63544420316565556ed28485e2" 265 | dependencies = [ 266 | "serde", 267 | ] 268 | 269 | [[package]] 270 | name = "cargo_metadata" 271 | version = "0.23.1" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "ef987d17b0a113becdd19d3d0022d04d7ef41f9efe4f3fb63ac44ba61df3ade9" 274 | dependencies = [ 275 | "camino", 276 | "cargo-platform", 277 | "semver", 278 | "serde", 279 | "serde_json", 280 | "thiserror 2.0.16", 281 | ] 282 | 283 | [[package]] 284 | name = "cc" 285 | version = "1.2.33" 286 | source = "registry+https://github.com/rust-lang/crates.io-index" 287 | checksum = "3ee0f8803222ba5a7e2777dd72ca451868909b1ac410621b676adf07280e9b5f" 288 | dependencies = [ 289 | "shlex", 290 | ] 291 | 292 | [[package]] 293 | name = "cfg-if" 294 | version = "1.0.3" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" 297 | 298 | [[package]] 299 | name = "clap" 300 | version = "4.5.45" 301 | source = "registry+https://github.com/rust-lang/crates.io-index" 302 | checksum = "1fc0e74a703892159f5ae7d3aac52c8e6c392f5ae5f359c70b5881d60aaac318" 303 | dependencies = [ 304 | "clap_builder", 305 | "clap_derive", 306 | ] 307 | 308 | [[package]] 309 | name = "clap_builder" 310 | version = "4.5.44" 311 | source = "registry+https://github.com/rust-lang/crates.io-index" 312 | checksum = "b3e7f4214277f3c7aa526a59dd3fbe306a370daee1f8b7b8c987069cd8e888a8" 313 | dependencies = [ 314 | "anstream", 315 | "anstyle", 316 | "clap_lex", 317 | "strsim", 318 | ] 319 | 320 | [[package]] 321 | name = "clap_complete" 322 | version = "4.5.57" 323 | source = "registry+https://github.com/rust-lang/crates.io-index" 324 | checksum = "4d9501bd3f5f09f7bbee01da9a511073ed30a80cd7a509f1214bb74eadea71ad" 325 | dependencies = [ 326 | "clap", 327 | ] 328 | 329 | [[package]] 330 | name = "clap_derive" 331 | version = "4.5.45" 332 | source = "registry+https://github.com/rust-lang/crates.io-index" 333 | checksum = "14cb31bb0a7d536caef2639baa7fad459e15c3144efefa6dbd1c84562c4739f6" 334 | dependencies = [ 335 | "heck", 336 | "proc-macro2", 337 | "quote", 338 | "syn", 339 | ] 340 | 341 | [[package]] 342 | name = "clap_lex" 343 | version = "0.7.5" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" 346 | 347 | [[package]] 348 | name = "colorchoice" 349 | version = "1.0.4" 350 | source = "registry+https://github.com/rust-lang/crates.io-index" 351 | checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" 352 | 353 | [[package]] 354 | name = "console" 355 | version = "0.16.0" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | checksum = "2e09ced7ebbccb63b4c65413d821f2e00ce54c5ca4514ddc6b3c892fdbcbc69d" 358 | dependencies = [ 359 | "encode_unicode", 360 | "libc", 361 | "once_cell", 362 | "unicode-width", 363 | "windows-sys 0.60.2", 364 | ] 365 | 366 | [[package]] 367 | name = "core-foundation" 368 | version = "0.9.4" 369 | source = "registry+https://github.com/rust-lang/crates.io-index" 370 | checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" 371 | dependencies = [ 372 | "core-foundation-sys", 373 | "libc", 374 | ] 375 | 376 | [[package]] 377 | name = "core-foundation-sys" 378 | version = "0.8.7" 379 | source = "registry+https://github.com/rust-lang/crates.io-index" 380 | checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 381 | 382 | [[package]] 383 | name = "crc32fast" 384 | version = "1.5.0" 385 | source = "registry+https://github.com/rust-lang/crates.io-index" 386 | checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" 387 | dependencies = [ 388 | "cfg-if", 389 | ] 390 | 391 | [[package]] 392 | name = "crossbeam-channel" 393 | version = "0.5.15" 394 | source = "registry+https://github.com/rust-lang/crates.io-index" 395 | checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" 396 | dependencies = [ 397 | "crossbeam-utils", 398 | ] 399 | 400 | [[package]] 401 | name = "crossbeam-utils" 402 | version = "0.8.21" 403 | source = "registry+https://github.com/rust-lang/crates.io-index" 404 | checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" 405 | 406 | [[package]] 407 | name = "dashmap" 408 | version = "6.1.0" 409 | source = "registry+https://github.com/rust-lang/crates.io-index" 410 | checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" 411 | dependencies = [ 412 | "cfg-if", 413 | "crossbeam-utils", 414 | "hashbrown 0.14.5", 415 | "lock_api", 416 | "once_cell", 417 | "parking_lot_core", 418 | ] 419 | 420 | [[package]] 421 | name = "deranged" 422 | version = "0.4.0" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" 425 | dependencies = [ 426 | "powerfmt", 427 | ] 428 | 429 | [[package]] 430 | name = "dirs" 431 | version = "4.0.0" 432 | source = "registry+https://github.com/rust-lang/crates.io-index" 433 | checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" 434 | dependencies = [ 435 | "dirs-sys", 436 | ] 437 | 438 | [[package]] 439 | name = "dirs-sys" 440 | version = "0.3.7" 441 | source = "registry+https://github.com/rust-lang/crates.io-index" 442 | checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" 443 | dependencies = [ 444 | "libc", 445 | "redox_users", 446 | "winapi", 447 | ] 448 | 449 | [[package]] 450 | name = "displaydoc" 451 | version = "0.2.5" 452 | source = "registry+https://github.com/rust-lang/crates.io-index" 453 | checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" 454 | dependencies = [ 455 | "proc-macro2", 456 | "quote", 457 | "syn", 458 | ] 459 | 460 | [[package]] 461 | name = "elsa" 462 | version = "1.11.2" 463 | source = "registry+https://github.com/rust-lang/crates.io-index" 464 | checksum = "9abf33c656a7256451ebb7d0082c5a471820c31269e49d807c538c252352186e" 465 | dependencies = [ 466 | "stable_deref_trait", 467 | ] 468 | 469 | [[package]] 470 | name = "encode_unicode" 471 | version = "1.0.0" 472 | source = "registry+https://github.com/rust-lang/crates.io-index" 473 | checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" 474 | 475 | [[package]] 476 | name = "encoding_rs" 477 | version = "0.8.35" 478 | source = "registry+https://github.com/rust-lang/crates.io-index" 479 | checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" 480 | dependencies = [ 481 | "cfg-if", 482 | ] 483 | 484 | [[package]] 485 | name = "equivalent" 486 | version = "1.0.2" 487 | source = "registry+https://github.com/rust-lang/crates.io-index" 488 | checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 489 | 490 | [[package]] 491 | name = "fallible-iterator" 492 | version = "0.2.0" 493 | source = "registry+https://github.com/rust-lang/crates.io-index" 494 | checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" 495 | 496 | [[package]] 497 | name = "flamegraph" 498 | version = "0.6.10" 499 | dependencies = [ 500 | "anyhow", 501 | "blondie", 502 | "cargo_metadata", 503 | "clap", 504 | "clap_complete", 505 | "indicatif", 506 | "inferno", 507 | "opener", 508 | "rustc-demangle", 509 | "shlex", 510 | "signal-hook", 511 | ] 512 | 513 | [[package]] 514 | name = "flate2" 515 | version = "1.1.2" 516 | source = "registry+https://github.com/rust-lang/crates.io-index" 517 | checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" 518 | dependencies = [ 519 | "crc32fast", 520 | "miniz_oxide", 521 | ] 522 | 523 | [[package]] 524 | name = "fnv" 525 | version = "1.0.7" 526 | source = "registry+https://github.com/rust-lang/crates.io-index" 527 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 528 | 529 | [[package]] 530 | name = "form_urlencoded" 531 | version = "1.2.1" 532 | source = "registry+https://github.com/rust-lang/crates.io-index" 533 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 534 | dependencies = [ 535 | "percent-encoding", 536 | ] 537 | 538 | [[package]] 539 | name = "futures-channel" 540 | version = "0.3.31" 541 | source = "registry+https://github.com/rust-lang/crates.io-index" 542 | checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 543 | dependencies = [ 544 | "futures-core", 545 | ] 546 | 547 | [[package]] 548 | name = "futures-core" 549 | version = "0.3.31" 550 | source = "registry+https://github.com/rust-lang/crates.io-index" 551 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 552 | 553 | [[package]] 554 | name = "futures-sink" 555 | version = "0.3.31" 556 | source = "registry+https://github.com/rust-lang/crates.io-index" 557 | checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 558 | 559 | [[package]] 560 | name = "futures-task" 561 | version = "0.3.31" 562 | source = "registry+https://github.com/rust-lang/crates.io-index" 563 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 564 | 565 | [[package]] 566 | name = "futures-util" 567 | version = "0.3.31" 568 | source = "registry+https://github.com/rust-lang/crates.io-index" 569 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 570 | dependencies = [ 571 | "futures-core", 572 | "futures-task", 573 | "pin-project-lite", 574 | "pin-utils", 575 | ] 576 | 577 | [[package]] 578 | name = "getrandom" 579 | version = "0.2.16" 580 | source = "registry+https://github.com/rust-lang/crates.io-index" 581 | checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" 582 | dependencies = [ 583 | "cfg-if", 584 | "libc", 585 | "wasi 0.11.1+wasi-snapshot-preview1", 586 | ] 587 | 588 | [[package]] 589 | name = "getrandom" 590 | version = "0.3.3" 591 | source = "registry+https://github.com/rust-lang/crates.io-index" 592 | checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" 593 | dependencies = [ 594 | "cfg-if", 595 | "libc", 596 | "r-efi", 597 | "wasi 0.14.2+wasi-0.2.4", 598 | ] 599 | 600 | [[package]] 601 | name = "gimli" 602 | version = "0.31.1" 603 | source = "registry+https://github.com/rust-lang/crates.io-index" 604 | checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 605 | 606 | [[package]] 607 | name = "h2" 608 | version = "0.3.27" 609 | source = "registry+https://github.com/rust-lang/crates.io-index" 610 | checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" 611 | dependencies = [ 612 | "bytes", 613 | "fnv", 614 | "futures-core", 615 | "futures-sink", 616 | "futures-util", 617 | "http", 618 | "indexmap", 619 | "slab", 620 | "tokio", 621 | "tokio-util", 622 | "tracing", 623 | ] 624 | 625 | [[package]] 626 | name = "hashbrown" 627 | version = "0.14.5" 628 | source = "registry+https://github.com/rust-lang/crates.io-index" 629 | checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" 630 | 631 | [[package]] 632 | name = "hashbrown" 633 | version = "0.15.5" 634 | source = "registry+https://github.com/rust-lang/crates.io-index" 635 | checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" 636 | 637 | [[package]] 638 | name = "heck" 639 | version = "0.5.0" 640 | source = "registry+https://github.com/rust-lang/crates.io-index" 641 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 642 | 643 | [[package]] 644 | name = "http" 645 | version = "0.2.12" 646 | source = "registry+https://github.com/rust-lang/crates.io-index" 647 | checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" 648 | dependencies = [ 649 | "bytes", 650 | "fnv", 651 | "itoa", 652 | ] 653 | 654 | [[package]] 655 | name = "http-body" 656 | version = "0.4.6" 657 | source = "registry+https://github.com/rust-lang/crates.io-index" 658 | checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" 659 | dependencies = [ 660 | "bytes", 661 | "http", 662 | "pin-project-lite", 663 | ] 664 | 665 | [[package]] 666 | name = "httparse" 667 | version = "1.10.1" 668 | source = "registry+https://github.com/rust-lang/crates.io-index" 669 | checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" 670 | 671 | [[package]] 672 | name = "httpdate" 673 | version = "1.0.3" 674 | source = "registry+https://github.com/rust-lang/crates.io-index" 675 | checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" 676 | 677 | [[package]] 678 | name = "hyper" 679 | version = "0.14.32" 680 | source = "registry+https://github.com/rust-lang/crates.io-index" 681 | checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" 682 | dependencies = [ 683 | "bytes", 684 | "futures-channel", 685 | "futures-core", 686 | "futures-util", 687 | "h2", 688 | "http", 689 | "http-body", 690 | "httparse", 691 | "httpdate", 692 | "itoa", 693 | "pin-project-lite", 694 | "socket2 0.5.10", 695 | "tokio", 696 | "tower-service", 697 | "tracing", 698 | "want", 699 | ] 700 | 701 | [[package]] 702 | name = "hyper-rustls" 703 | version = "0.24.2" 704 | source = "registry+https://github.com/rust-lang/crates.io-index" 705 | checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" 706 | dependencies = [ 707 | "futures-util", 708 | "http", 709 | "hyper", 710 | "rustls", 711 | "tokio", 712 | "tokio-rustls", 713 | ] 714 | 715 | [[package]] 716 | name = "icu_collections" 717 | version = "2.0.0" 718 | source = "registry+https://github.com/rust-lang/crates.io-index" 719 | checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" 720 | dependencies = [ 721 | "displaydoc", 722 | "potential_utf", 723 | "yoke", 724 | "zerofrom", 725 | "zerovec", 726 | ] 727 | 728 | [[package]] 729 | name = "icu_locale_core" 730 | version = "2.0.0" 731 | source = "registry+https://github.com/rust-lang/crates.io-index" 732 | checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" 733 | dependencies = [ 734 | "displaydoc", 735 | "litemap", 736 | "tinystr", 737 | "writeable", 738 | "zerovec", 739 | ] 740 | 741 | [[package]] 742 | name = "icu_normalizer" 743 | version = "2.0.0" 744 | source = "registry+https://github.com/rust-lang/crates.io-index" 745 | checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" 746 | dependencies = [ 747 | "displaydoc", 748 | "icu_collections", 749 | "icu_normalizer_data", 750 | "icu_properties", 751 | "icu_provider", 752 | "smallvec", 753 | "zerovec", 754 | ] 755 | 756 | [[package]] 757 | name = "icu_normalizer_data" 758 | version = "2.0.0" 759 | source = "registry+https://github.com/rust-lang/crates.io-index" 760 | checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" 761 | 762 | [[package]] 763 | name = "icu_properties" 764 | version = "2.0.1" 765 | source = "registry+https://github.com/rust-lang/crates.io-index" 766 | checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" 767 | dependencies = [ 768 | "displaydoc", 769 | "icu_collections", 770 | "icu_locale_core", 771 | "icu_properties_data", 772 | "icu_provider", 773 | "potential_utf", 774 | "zerotrie", 775 | "zerovec", 776 | ] 777 | 778 | [[package]] 779 | name = "icu_properties_data" 780 | version = "2.0.1" 781 | source = "registry+https://github.com/rust-lang/crates.io-index" 782 | checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" 783 | 784 | [[package]] 785 | name = "icu_provider" 786 | version = "2.0.0" 787 | source = "registry+https://github.com/rust-lang/crates.io-index" 788 | checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" 789 | dependencies = [ 790 | "displaydoc", 791 | "icu_locale_core", 792 | "stable_deref_trait", 793 | "tinystr", 794 | "writeable", 795 | "yoke", 796 | "zerofrom", 797 | "zerotrie", 798 | "zerovec", 799 | ] 800 | 801 | [[package]] 802 | name = "idna" 803 | version = "1.0.3" 804 | source = "registry+https://github.com/rust-lang/crates.io-index" 805 | checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" 806 | dependencies = [ 807 | "idna_adapter", 808 | "smallvec", 809 | "utf8_iter", 810 | ] 811 | 812 | [[package]] 813 | name = "idna_adapter" 814 | version = "1.2.1" 815 | source = "registry+https://github.com/rust-lang/crates.io-index" 816 | checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" 817 | dependencies = [ 818 | "icu_normalizer", 819 | "icu_properties", 820 | ] 821 | 822 | [[package]] 823 | name = "indexmap" 824 | version = "2.10.0" 825 | source = "registry+https://github.com/rust-lang/crates.io-index" 826 | checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" 827 | dependencies = [ 828 | "equivalent", 829 | "hashbrown 0.15.5", 830 | ] 831 | 832 | [[package]] 833 | name = "indicatif" 834 | version = "0.18.0" 835 | source = "registry+https://github.com/rust-lang/crates.io-index" 836 | checksum = "70a646d946d06bedbbc4cac4c218acf4bbf2d87757a784857025f4d447e4e1cd" 837 | dependencies = [ 838 | "console", 839 | "portable-atomic", 840 | "unicode-width", 841 | "unit-prefix", 842 | "web-time", 843 | ] 844 | 845 | [[package]] 846 | name = "inferno" 847 | version = "0.12.3" 848 | source = "registry+https://github.com/rust-lang/crates.io-index" 849 | checksum = "e96d2465363ed2d81857759fc864cf6bb7997f79327aec028d65bd7989393685" 850 | dependencies = [ 851 | "ahash", 852 | "crossbeam-channel", 853 | "crossbeam-utils", 854 | "dashmap", 855 | "indexmap", 856 | "itoa", 857 | "log", 858 | "num-format", 859 | "once_cell", 860 | "quick-xml", 861 | "rgb", 862 | "str_stack", 863 | ] 864 | 865 | [[package]] 866 | name = "inplace-vec-builder" 867 | version = "0.1.1" 868 | source = "registry+https://github.com/rust-lang/crates.io-index" 869 | checksum = "cf64c2edc8226891a71f127587a2861b132d2b942310843814d5001d99a1d307" 870 | dependencies = [ 871 | "smallvec", 872 | ] 873 | 874 | [[package]] 875 | name = "io-uring" 876 | version = "0.7.9" 877 | source = "registry+https://github.com/rust-lang/crates.io-index" 878 | checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" 879 | dependencies = [ 880 | "bitflags 2.9.2", 881 | "cfg-if", 882 | "libc", 883 | ] 884 | 885 | [[package]] 886 | name = "ipnet" 887 | version = "2.11.0" 888 | source = "registry+https://github.com/rust-lang/crates.io-index" 889 | checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" 890 | 891 | [[package]] 892 | name = "is_terminal_polyfill" 893 | version = "1.70.1" 894 | source = "registry+https://github.com/rust-lang/crates.io-index" 895 | checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 896 | 897 | [[package]] 898 | name = "itoa" 899 | version = "1.0.15" 900 | source = "registry+https://github.com/rust-lang/crates.io-index" 901 | checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 902 | 903 | [[package]] 904 | name = "js-sys" 905 | version = "0.3.77" 906 | source = "registry+https://github.com/rust-lang/crates.io-index" 907 | checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" 908 | dependencies = [ 909 | "once_cell", 910 | "wasm-bindgen", 911 | ] 912 | 913 | [[package]] 914 | name = "libc" 915 | version = "0.2.175" 916 | source = "registry+https://github.com/rust-lang/crates.io-index" 917 | checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" 918 | 919 | [[package]] 920 | name = "libredox" 921 | version = "0.1.9" 922 | source = "registry+https://github.com/rust-lang/crates.io-index" 923 | checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" 924 | dependencies = [ 925 | "bitflags 2.9.2", 926 | "libc", 927 | ] 928 | 929 | [[package]] 930 | name = "litemap" 931 | version = "0.8.0" 932 | source = "registry+https://github.com/rust-lang/crates.io-index" 933 | checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" 934 | 935 | [[package]] 936 | name = "lock_api" 937 | version = "0.4.13" 938 | source = "registry+https://github.com/rust-lang/crates.io-index" 939 | checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" 940 | dependencies = [ 941 | "autocfg", 942 | "scopeguard", 943 | ] 944 | 945 | [[package]] 946 | name = "log" 947 | version = "0.4.27" 948 | source = "registry+https://github.com/rust-lang/crates.io-index" 949 | checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 950 | 951 | [[package]] 952 | name = "lzxd" 953 | version = "0.1.4" 954 | source = "registry+https://github.com/rust-lang/crates.io-index" 955 | checksum = "784462f20dddd9dfdb45de963fa4ad4a288cb10a7889ac5d2c34fb6481c6b213" 956 | 957 | [[package]] 958 | name = "maybe-owned" 959 | version = "0.3.4" 960 | source = "registry+https://github.com/rust-lang/crates.io-index" 961 | checksum = "4facc753ae494aeb6e3c22f839b158aebd4f9270f55cd3c79906c45476c47ab4" 962 | 963 | [[package]] 964 | name = "memchr" 965 | version = "2.7.5" 966 | source = "registry+https://github.com/rust-lang/crates.io-index" 967 | checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" 968 | 969 | [[package]] 970 | name = "memmap2" 971 | version = "0.5.10" 972 | source = "registry+https://github.com/rust-lang/crates.io-index" 973 | checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" 974 | dependencies = [ 975 | "libc", 976 | ] 977 | 978 | [[package]] 979 | name = "mime" 980 | version = "0.3.17" 981 | source = "registry+https://github.com/rust-lang/crates.io-index" 982 | checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 983 | 984 | [[package]] 985 | name = "miniz_oxide" 986 | version = "0.8.9" 987 | source = "registry+https://github.com/rust-lang/crates.io-index" 988 | checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" 989 | dependencies = [ 990 | "adler2", 991 | ] 992 | 993 | [[package]] 994 | name = "mio" 995 | version = "1.0.4" 996 | source = "registry+https://github.com/rust-lang/crates.io-index" 997 | checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" 998 | dependencies = [ 999 | "libc", 1000 | "wasi 0.11.1+wasi-snapshot-preview1", 1001 | "windows-sys 0.59.0", 1002 | ] 1003 | 1004 | [[package]] 1005 | name = "normpath" 1006 | version = "1.3.0" 1007 | source = "registry+https://github.com/rust-lang/crates.io-index" 1008 | checksum = "c8911957c4b1549ac0dc74e30db9c8b0e66ddcd6d7acc33098f4c63a64a6d7ed" 1009 | dependencies = [ 1010 | "windows-sys 0.59.0", 1011 | ] 1012 | 1013 | [[package]] 1014 | name = "num-conv" 1015 | version = "0.1.0" 1016 | source = "registry+https://github.com/rust-lang/crates.io-index" 1017 | checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" 1018 | 1019 | [[package]] 1020 | name = "num-format" 1021 | version = "0.4.4" 1022 | source = "registry+https://github.com/rust-lang/crates.io-index" 1023 | checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" 1024 | dependencies = [ 1025 | "arrayvec", 1026 | "itoa", 1027 | ] 1028 | 1029 | [[package]] 1030 | name = "object" 1031 | version = "0.30.4" 1032 | source = "registry+https://github.com/rust-lang/crates.io-index" 1033 | checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" 1034 | dependencies = [ 1035 | "flate2", 1036 | "memchr", 1037 | ] 1038 | 1039 | [[package]] 1040 | name = "object" 1041 | version = "0.36.7" 1042 | source = "registry+https://github.com/rust-lang/crates.io-index" 1043 | checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" 1044 | dependencies = [ 1045 | "memchr", 1046 | ] 1047 | 1048 | [[package]] 1049 | name = "once_cell" 1050 | version = "1.21.3" 1051 | source = "registry+https://github.com/rust-lang/crates.io-index" 1052 | checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 1053 | 1054 | [[package]] 1055 | name = "once_cell_polyfill" 1056 | version = "1.70.1" 1057 | source = "registry+https://github.com/rust-lang/crates.io-index" 1058 | checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" 1059 | 1060 | [[package]] 1061 | name = "opener" 1062 | version = "0.8.2" 1063 | source = "registry+https://github.com/rust-lang/crates.io-index" 1064 | checksum = "771b9704f8cd8b424ec747a320b30b47517a6966ba2c7da90047c16f4a962223" 1065 | dependencies = [ 1066 | "bstr", 1067 | "normpath", 1068 | "windows-sys 0.59.0", 1069 | ] 1070 | 1071 | [[package]] 1072 | name = "parking_lot_core" 1073 | version = "0.9.11" 1074 | source = "registry+https://github.com/rust-lang/crates.io-index" 1075 | checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" 1076 | dependencies = [ 1077 | "cfg-if", 1078 | "libc", 1079 | "redox_syscall", 1080 | "smallvec", 1081 | "windows-targets 0.52.6", 1082 | ] 1083 | 1084 | [[package]] 1085 | name = "pdb" 1086 | version = "0.8.0" 1087 | source = "registry+https://github.com/rust-lang/crates.io-index" 1088 | checksum = "82040a392923abe6279c00ab4aff62d5250d1c8555dc780e4b02783a7aa74863" 1089 | dependencies = [ 1090 | "fallible-iterator", 1091 | "scroll", 1092 | "uuid", 1093 | ] 1094 | 1095 | [[package]] 1096 | name = "pdb-addr2line" 1097 | version = "0.10.4" 1098 | source = "registry+https://github.com/rust-lang/crates.io-index" 1099 | checksum = "c4e89a9f2f40b2389ba6da0814c8044bf942bece03dffa1514f84e3b525f4f9a" 1100 | dependencies = [ 1101 | "bitflags 1.3.2", 1102 | "elsa", 1103 | "maybe-owned", 1104 | "pdb", 1105 | "range-collections", 1106 | "thiserror 1.0.69", 1107 | ] 1108 | 1109 | [[package]] 1110 | name = "percent-encoding" 1111 | version = "2.3.1" 1112 | source = "registry+https://github.com/rust-lang/crates.io-index" 1113 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 1114 | 1115 | [[package]] 1116 | name = "pin-project-lite" 1117 | version = "0.2.16" 1118 | source = "registry+https://github.com/rust-lang/crates.io-index" 1119 | checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 1120 | 1121 | [[package]] 1122 | name = "pin-utils" 1123 | version = "0.1.0" 1124 | source = "registry+https://github.com/rust-lang/crates.io-index" 1125 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1126 | 1127 | [[package]] 1128 | name = "portable-atomic" 1129 | version = "1.11.1" 1130 | source = "registry+https://github.com/rust-lang/crates.io-index" 1131 | checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" 1132 | 1133 | [[package]] 1134 | name = "potential_utf" 1135 | version = "0.1.2" 1136 | source = "registry+https://github.com/rust-lang/crates.io-index" 1137 | checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" 1138 | dependencies = [ 1139 | "zerovec", 1140 | ] 1141 | 1142 | [[package]] 1143 | name = "powerfmt" 1144 | version = "0.2.0" 1145 | source = "registry+https://github.com/rust-lang/crates.io-index" 1146 | checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" 1147 | 1148 | [[package]] 1149 | name = "proc-macro2" 1150 | version = "1.0.101" 1151 | source = "registry+https://github.com/rust-lang/crates.io-index" 1152 | checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" 1153 | dependencies = [ 1154 | "unicode-ident", 1155 | ] 1156 | 1157 | [[package]] 1158 | name = "quick-xml" 1159 | version = "0.37.5" 1160 | source = "registry+https://github.com/rust-lang/crates.io-index" 1161 | checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" 1162 | dependencies = [ 1163 | "memchr", 1164 | ] 1165 | 1166 | [[package]] 1167 | name = "quote" 1168 | version = "1.0.40" 1169 | source = "registry+https://github.com/rust-lang/crates.io-index" 1170 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 1171 | dependencies = [ 1172 | "proc-macro2", 1173 | ] 1174 | 1175 | [[package]] 1176 | name = "r-efi" 1177 | version = "5.3.0" 1178 | source = "registry+https://github.com/rust-lang/crates.io-index" 1179 | checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" 1180 | 1181 | [[package]] 1182 | name = "range-collections" 1183 | version = "0.2.4" 1184 | source = "registry+https://github.com/rust-lang/crates.io-index" 1185 | checksum = "61fdfd79629e2b44a1d34b4d227957174cb858e6b86ee45fad114edbcfc903ab" 1186 | dependencies = [ 1187 | "binary-merge", 1188 | "inplace-vec-builder", 1189 | "smallvec", 1190 | ] 1191 | 1192 | [[package]] 1193 | name = "redox_syscall" 1194 | version = "0.5.17" 1195 | source = "registry+https://github.com/rust-lang/crates.io-index" 1196 | checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" 1197 | dependencies = [ 1198 | "bitflags 2.9.2", 1199 | ] 1200 | 1201 | [[package]] 1202 | name = "redox_users" 1203 | version = "0.4.6" 1204 | source = "registry+https://github.com/rust-lang/crates.io-index" 1205 | checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" 1206 | dependencies = [ 1207 | "getrandom 0.2.16", 1208 | "libredox", 1209 | "thiserror 1.0.69", 1210 | ] 1211 | 1212 | [[package]] 1213 | name = "regex-automata" 1214 | version = "0.4.9" 1215 | source = "registry+https://github.com/rust-lang/crates.io-index" 1216 | checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 1217 | 1218 | [[package]] 1219 | name = "reqwest" 1220 | version = "0.11.27" 1221 | source = "registry+https://github.com/rust-lang/crates.io-index" 1222 | checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" 1223 | dependencies = [ 1224 | "async-compression", 1225 | "base64", 1226 | "bytes", 1227 | "encoding_rs", 1228 | "futures-core", 1229 | "futures-util", 1230 | "h2", 1231 | "http", 1232 | "http-body", 1233 | "hyper", 1234 | "hyper-rustls", 1235 | "ipnet", 1236 | "js-sys", 1237 | "log", 1238 | "mime", 1239 | "once_cell", 1240 | "percent-encoding", 1241 | "pin-project-lite", 1242 | "rustls", 1243 | "rustls-pemfile", 1244 | "serde", 1245 | "serde_json", 1246 | "serde_urlencoded", 1247 | "sync_wrapper", 1248 | "system-configuration", 1249 | "tokio", 1250 | "tokio-rustls", 1251 | "tokio-util", 1252 | "tower-service", 1253 | "url", 1254 | "wasm-bindgen", 1255 | "wasm-bindgen-futures", 1256 | "web-sys", 1257 | "webpki-roots", 1258 | "winreg", 1259 | ] 1260 | 1261 | [[package]] 1262 | name = "rgb" 1263 | version = "0.8.52" 1264 | source = "registry+https://github.com/rust-lang/crates.io-index" 1265 | checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce" 1266 | dependencies = [ 1267 | "bytemuck", 1268 | ] 1269 | 1270 | [[package]] 1271 | name = "ring" 1272 | version = "0.17.14" 1273 | source = "registry+https://github.com/rust-lang/crates.io-index" 1274 | checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" 1275 | dependencies = [ 1276 | "cc", 1277 | "cfg-if", 1278 | "getrandom 0.2.16", 1279 | "libc", 1280 | "untrusted", 1281 | "windows-sys 0.52.0", 1282 | ] 1283 | 1284 | [[package]] 1285 | name = "rustc-demangle" 1286 | version = "0.1.26" 1287 | source = "registry+https://github.com/rust-lang/crates.io-index" 1288 | checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" 1289 | 1290 | [[package]] 1291 | name = "rustc-hash" 1292 | version = "1.1.0" 1293 | source = "registry+https://github.com/rust-lang/crates.io-index" 1294 | checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 1295 | 1296 | [[package]] 1297 | name = "rustls" 1298 | version = "0.21.12" 1299 | source = "registry+https://github.com/rust-lang/crates.io-index" 1300 | checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" 1301 | dependencies = [ 1302 | "log", 1303 | "ring", 1304 | "rustls-webpki", 1305 | "sct", 1306 | ] 1307 | 1308 | [[package]] 1309 | name = "rustls-pemfile" 1310 | version = "1.0.4" 1311 | source = "registry+https://github.com/rust-lang/crates.io-index" 1312 | checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" 1313 | dependencies = [ 1314 | "base64", 1315 | ] 1316 | 1317 | [[package]] 1318 | name = "rustls-webpki" 1319 | version = "0.101.7" 1320 | source = "registry+https://github.com/rust-lang/crates.io-index" 1321 | checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" 1322 | dependencies = [ 1323 | "ring", 1324 | "untrusted", 1325 | ] 1326 | 1327 | [[package]] 1328 | name = "rustversion" 1329 | version = "1.0.22" 1330 | source = "registry+https://github.com/rust-lang/crates.io-index" 1331 | checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" 1332 | 1333 | [[package]] 1334 | name = "ryu" 1335 | version = "1.0.20" 1336 | source = "registry+https://github.com/rust-lang/crates.io-index" 1337 | checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 1338 | 1339 | [[package]] 1340 | name = "scopeguard" 1341 | version = "1.2.0" 1342 | source = "registry+https://github.com/rust-lang/crates.io-index" 1343 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 1344 | 1345 | [[package]] 1346 | name = "scroll" 1347 | version = "0.11.0" 1348 | source = "registry+https://github.com/rust-lang/crates.io-index" 1349 | checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da" 1350 | 1351 | [[package]] 1352 | name = "sct" 1353 | version = "0.7.1" 1354 | source = "registry+https://github.com/rust-lang/crates.io-index" 1355 | checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" 1356 | dependencies = [ 1357 | "ring", 1358 | "untrusted", 1359 | ] 1360 | 1361 | [[package]] 1362 | name = "semver" 1363 | version = "1.0.26" 1364 | source = "registry+https://github.com/rust-lang/crates.io-index" 1365 | checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" 1366 | dependencies = [ 1367 | "serde", 1368 | ] 1369 | 1370 | [[package]] 1371 | name = "serde" 1372 | version = "1.0.219" 1373 | source = "registry+https://github.com/rust-lang/crates.io-index" 1374 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 1375 | dependencies = [ 1376 | "serde_derive", 1377 | ] 1378 | 1379 | [[package]] 1380 | name = "serde_derive" 1381 | version = "1.0.219" 1382 | source = "registry+https://github.com/rust-lang/crates.io-index" 1383 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 1384 | dependencies = [ 1385 | "proc-macro2", 1386 | "quote", 1387 | "syn", 1388 | ] 1389 | 1390 | [[package]] 1391 | name = "serde_json" 1392 | version = "1.0.143" 1393 | source = "registry+https://github.com/rust-lang/crates.io-index" 1394 | checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" 1395 | dependencies = [ 1396 | "itoa", 1397 | "memchr", 1398 | "ryu", 1399 | "serde", 1400 | ] 1401 | 1402 | [[package]] 1403 | name = "serde_urlencoded" 1404 | version = "0.7.1" 1405 | source = "registry+https://github.com/rust-lang/crates.io-index" 1406 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 1407 | dependencies = [ 1408 | "form_urlencoded", 1409 | "itoa", 1410 | "ryu", 1411 | "serde", 1412 | ] 1413 | 1414 | [[package]] 1415 | name = "shlex" 1416 | version = "1.3.0" 1417 | source = "registry+https://github.com/rust-lang/crates.io-index" 1418 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 1419 | 1420 | [[package]] 1421 | name = "signal-hook" 1422 | version = "0.3.18" 1423 | source = "registry+https://github.com/rust-lang/crates.io-index" 1424 | checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" 1425 | dependencies = [ 1426 | "libc", 1427 | "signal-hook-registry", 1428 | ] 1429 | 1430 | [[package]] 1431 | name = "signal-hook-registry" 1432 | version = "1.4.6" 1433 | source = "registry+https://github.com/rust-lang/crates.io-index" 1434 | checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" 1435 | dependencies = [ 1436 | "libc", 1437 | ] 1438 | 1439 | [[package]] 1440 | name = "slab" 1441 | version = "0.4.11" 1442 | source = "registry+https://github.com/rust-lang/crates.io-index" 1443 | checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" 1444 | 1445 | [[package]] 1446 | name = "smallvec" 1447 | version = "1.15.1" 1448 | source = "registry+https://github.com/rust-lang/crates.io-index" 1449 | checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" 1450 | 1451 | [[package]] 1452 | name = "socket2" 1453 | version = "0.5.10" 1454 | source = "registry+https://github.com/rust-lang/crates.io-index" 1455 | checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" 1456 | dependencies = [ 1457 | "libc", 1458 | "windows-sys 0.52.0", 1459 | ] 1460 | 1461 | [[package]] 1462 | name = "socket2" 1463 | version = "0.6.0" 1464 | source = "registry+https://github.com/rust-lang/crates.io-index" 1465 | checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" 1466 | dependencies = [ 1467 | "libc", 1468 | "windows-sys 0.59.0", 1469 | ] 1470 | 1471 | [[package]] 1472 | name = "stable_deref_trait" 1473 | version = "1.2.0" 1474 | source = "registry+https://github.com/rust-lang/crates.io-index" 1475 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 1476 | 1477 | [[package]] 1478 | name = "str_stack" 1479 | version = "0.1.0" 1480 | source = "registry+https://github.com/rust-lang/crates.io-index" 1481 | checksum = "9091b6114800a5f2141aee1d1b9d6ca3592ac062dc5decb3764ec5895a47b4eb" 1482 | 1483 | [[package]] 1484 | name = "strsim" 1485 | version = "0.11.1" 1486 | source = "registry+https://github.com/rust-lang/crates.io-index" 1487 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 1488 | 1489 | [[package]] 1490 | name = "symsrv" 1491 | version = "0.2.0" 1492 | source = "registry+https://github.com/rust-lang/crates.io-index" 1493 | checksum = "328d40bbd6972015696ee86709ccc1327a2529816898f2844a62b71e449b1274" 1494 | dependencies = [ 1495 | "bytes", 1496 | "cab", 1497 | "dirs", 1498 | "memmap2", 1499 | "reqwest", 1500 | "thiserror 1.0.69", 1501 | "tokio", 1502 | ] 1503 | 1504 | [[package]] 1505 | name = "syn" 1506 | version = "2.0.106" 1507 | source = "registry+https://github.com/rust-lang/crates.io-index" 1508 | checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" 1509 | dependencies = [ 1510 | "proc-macro2", 1511 | "quote", 1512 | "unicode-ident", 1513 | ] 1514 | 1515 | [[package]] 1516 | name = "sync_wrapper" 1517 | version = "0.1.2" 1518 | source = "registry+https://github.com/rust-lang/crates.io-index" 1519 | checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" 1520 | 1521 | [[package]] 1522 | name = "synstructure" 1523 | version = "0.13.2" 1524 | source = "registry+https://github.com/rust-lang/crates.io-index" 1525 | checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" 1526 | dependencies = [ 1527 | "proc-macro2", 1528 | "quote", 1529 | "syn", 1530 | ] 1531 | 1532 | [[package]] 1533 | name = "system-configuration" 1534 | version = "0.5.1" 1535 | source = "registry+https://github.com/rust-lang/crates.io-index" 1536 | checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" 1537 | dependencies = [ 1538 | "bitflags 1.3.2", 1539 | "core-foundation", 1540 | "system-configuration-sys", 1541 | ] 1542 | 1543 | [[package]] 1544 | name = "system-configuration-sys" 1545 | version = "0.5.0" 1546 | source = "registry+https://github.com/rust-lang/crates.io-index" 1547 | checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" 1548 | dependencies = [ 1549 | "core-foundation-sys", 1550 | "libc", 1551 | ] 1552 | 1553 | [[package]] 1554 | name = "thiserror" 1555 | version = "1.0.69" 1556 | source = "registry+https://github.com/rust-lang/crates.io-index" 1557 | checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" 1558 | dependencies = [ 1559 | "thiserror-impl 1.0.69", 1560 | ] 1561 | 1562 | [[package]] 1563 | name = "thiserror" 1564 | version = "2.0.16" 1565 | source = "registry+https://github.com/rust-lang/crates.io-index" 1566 | checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" 1567 | dependencies = [ 1568 | "thiserror-impl 2.0.16", 1569 | ] 1570 | 1571 | [[package]] 1572 | name = "thiserror-impl" 1573 | version = "1.0.69" 1574 | source = "registry+https://github.com/rust-lang/crates.io-index" 1575 | checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" 1576 | dependencies = [ 1577 | "proc-macro2", 1578 | "quote", 1579 | "syn", 1580 | ] 1581 | 1582 | [[package]] 1583 | name = "thiserror-impl" 1584 | version = "2.0.16" 1585 | source = "registry+https://github.com/rust-lang/crates.io-index" 1586 | checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" 1587 | dependencies = [ 1588 | "proc-macro2", 1589 | "quote", 1590 | "syn", 1591 | ] 1592 | 1593 | [[package]] 1594 | name = "time" 1595 | version = "0.3.41" 1596 | source = "registry+https://github.com/rust-lang/crates.io-index" 1597 | checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" 1598 | dependencies = [ 1599 | "deranged", 1600 | "num-conv", 1601 | "powerfmt", 1602 | "serde", 1603 | "time-core", 1604 | ] 1605 | 1606 | [[package]] 1607 | name = "time-core" 1608 | version = "0.1.4" 1609 | source = "registry+https://github.com/rust-lang/crates.io-index" 1610 | checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" 1611 | 1612 | [[package]] 1613 | name = "tinystr" 1614 | version = "0.8.1" 1615 | source = "registry+https://github.com/rust-lang/crates.io-index" 1616 | checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" 1617 | dependencies = [ 1618 | "displaydoc", 1619 | "zerovec", 1620 | ] 1621 | 1622 | [[package]] 1623 | name = "tokio" 1624 | version = "1.47.1" 1625 | source = "registry+https://github.com/rust-lang/crates.io-index" 1626 | checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" 1627 | dependencies = [ 1628 | "backtrace", 1629 | "bytes", 1630 | "io-uring", 1631 | "libc", 1632 | "mio", 1633 | "pin-project-lite", 1634 | "slab", 1635 | "socket2 0.6.0", 1636 | "windows-sys 0.59.0", 1637 | ] 1638 | 1639 | [[package]] 1640 | name = "tokio-rustls" 1641 | version = "0.24.1" 1642 | source = "registry+https://github.com/rust-lang/crates.io-index" 1643 | checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" 1644 | dependencies = [ 1645 | "rustls", 1646 | "tokio", 1647 | ] 1648 | 1649 | [[package]] 1650 | name = "tokio-util" 1651 | version = "0.7.16" 1652 | source = "registry+https://github.com/rust-lang/crates.io-index" 1653 | checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" 1654 | dependencies = [ 1655 | "bytes", 1656 | "futures-core", 1657 | "futures-sink", 1658 | "pin-project-lite", 1659 | "tokio", 1660 | ] 1661 | 1662 | [[package]] 1663 | name = "tower-service" 1664 | version = "0.3.3" 1665 | source = "registry+https://github.com/rust-lang/crates.io-index" 1666 | checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" 1667 | 1668 | [[package]] 1669 | name = "tracing" 1670 | version = "0.1.41" 1671 | source = "registry+https://github.com/rust-lang/crates.io-index" 1672 | checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" 1673 | dependencies = [ 1674 | "pin-project-lite", 1675 | "tracing-core", 1676 | ] 1677 | 1678 | [[package]] 1679 | name = "tracing-core" 1680 | version = "0.1.34" 1681 | source = "registry+https://github.com/rust-lang/crates.io-index" 1682 | checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" 1683 | dependencies = [ 1684 | "once_cell", 1685 | ] 1686 | 1687 | [[package]] 1688 | name = "try-lock" 1689 | version = "0.2.5" 1690 | source = "registry+https://github.com/rust-lang/crates.io-index" 1691 | checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" 1692 | 1693 | [[package]] 1694 | name = "unicode-ident" 1695 | version = "1.0.18" 1696 | source = "registry+https://github.com/rust-lang/crates.io-index" 1697 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 1698 | 1699 | [[package]] 1700 | name = "unicode-width" 1701 | version = "0.2.1" 1702 | source = "registry+https://github.com/rust-lang/crates.io-index" 1703 | checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" 1704 | 1705 | [[package]] 1706 | name = "unit-prefix" 1707 | version = "0.5.1" 1708 | source = "registry+https://github.com/rust-lang/crates.io-index" 1709 | checksum = "323402cff2dd658f39ca17c789b502021b3f18707c91cdf22e3838e1b4023817" 1710 | 1711 | [[package]] 1712 | name = "untrusted" 1713 | version = "0.9.0" 1714 | source = "registry+https://github.com/rust-lang/crates.io-index" 1715 | checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 1716 | 1717 | [[package]] 1718 | name = "url" 1719 | version = "2.5.4" 1720 | source = "registry+https://github.com/rust-lang/crates.io-index" 1721 | checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" 1722 | dependencies = [ 1723 | "form_urlencoded", 1724 | "idna", 1725 | "percent-encoding", 1726 | ] 1727 | 1728 | [[package]] 1729 | name = "utf8_iter" 1730 | version = "1.0.4" 1731 | source = "registry+https://github.com/rust-lang/crates.io-index" 1732 | checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" 1733 | 1734 | [[package]] 1735 | name = "utf8parse" 1736 | version = "0.2.2" 1737 | source = "registry+https://github.com/rust-lang/crates.io-index" 1738 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 1739 | 1740 | [[package]] 1741 | name = "uuid" 1742 | version = "1.18.0" 1743 | source = "registry+https://github.com/rust-lang/crates.io-index" 1744 | checksum = "f33196643e165781c20a5ead5582283a7dacbb87855d867fbc2df3f81eddc1be" 1745 | dependencies = [ 1746 | "js-sys", 1747 | "wasm-bindgen", 1748 | ] 1749 | 1750 | [[package]] 1751 | name = "version_check" 1752 | version = "0.9.5" 1753 | source = "registry+https://github.com/rust-lang/crates.io-index" 1754 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 1755 | 1756 | [[package]] 1757 | name = "want" 1758 | version = "0.3.1" 1759 | source = "registry+https://github.com/rust-lang/crates.io-index" 1760 | checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" 1761 | dependencies = [ 1762 | "try-lock", 1763 | ] 1764 | 1765 | [[package]] 1766 | name = "wasi" 1767 | version = "0.11.1+wasi-snapshot-preview1" 1768 | source = "registry+https://github.com/rust-lang/crates.io-index" 1769 | checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" 1770 | 1771 | [[package]] 1772 | name = "wasi" 1773 | version = "0.14.2+wasi-0.2.4" 1774 | source = "registry+https://github.com/rust-lang/crates.io-index" 1775 | checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" 1776 | dependencies = [ 1777 | "wit-bindgen-rt", 1778 | ] 1779 | 1780 | [[package]] 1781 | name = "wasm-bindgen" 1782 | version = "0.2.100" 1783 | source = "registry+https://github.com/rust-lang/crates.io-index" 1784 | checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" 1785 | dependencies = [ 1786 | "cfg-if", 1787 | "once_cell", 1788 | "rustversion", 1789 | "wasm-bindgen-macro", 1790 | ] 1791 | 1792 | [[package]] 1793 | name = "wasm-bindgen-backend" 1794 | version = "0.2.100" 1795 | source = "registry+https://github.com/rust-lang/crates.io-index" 1796 | checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" 1797 | dependencies = [ 1798 | "bumpalo", 1799 | "log", 1800 | "proc-macro2", 1801 | "quote", 1802 | "syn", 1803 | "wasm-bindgen-shared", 1804 | ] 1805 | 1806 | [[package]] 1807 | name = "wasm-bindgen-futures" 1808 | version = "0.4.50" 1809 | source = "registry+https://github.com/rust-lang/crates.io-index" 1810 | checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" 1811 | dependencies = [ 1812 | "cfg-if", 1813 | "js-sys", 1814 | "once_cell", 1815 | "wasm-bindgen", 1816 | "web-sys", 1817 | ] 1818 | 1819 | [[package]] 1820 | name = "wasm-bindgen-macro" 1821 | version = "0.2.100" 1822 | source = "registry+https://github.com/rust-lang/crates.io-index" 1823 | checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" 1824 | dependencies = [ 1825 | "quote", 1826 | "wasm-bindgen-macro-support", 1827 | ] 1828 | 1829 | [[package]] 1830 | name = "wasm-bindgen-macro-support" 1831 | version = "0.2.100" 1832 | source = "registry+https://github.com/rust-lang/crates.io-index" 1833 | checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" 1834 | dependencies = [ 1835 | "proc-macro2", 1836 | "quote", 1837 | "syn", 1838 | "wasm-bindgen-backend", 1839 | "wasm-bindgen-shared", 1840 | ] 1841 | 1842 | [[package]] 1843 | name = "wasm-bindgen-shared" 1844 | version = "0.2.100" 1845 | source = "registry+https://github.com/rust-lang/crates.io-index" 1846 | checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" 1847 | dependencies = [ 1848 | "unicode-ident", 1849 | ] 1850 | 1851 | [[package]] 1852 | name = "web-sys" 1853 | version = "0.3.77" 1854 | source = "registry+https://github.com/rust-lang/crates.io-index" 1855 | checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" 1856 | dependencies = [ 1857 | "js-sys", 1858 | "wasm-bindgen", 1859 | ] 1860 | 1861 | [[package]] 1862 | name = "web-time" 1863 | version = "1.1.0" 1864 | source = "registry+https://github.com/rust-lang/crates.io-index" 1865 | checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" 1866 | dependencies = [ 1867 | "js-sys", 1868 | "wasm-bindgen", 1869 | ] 1870 | 1871 | [[package]] 1872 | name = "webpki-roots" 1873 | version = "0.25.4" 1874 | source = "registry+https://github.com/rust-lang/crates.io-index" 1875 | checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" 1876 | 1877 | [[package]] 1878 | name = "winapi" 1879 | version = "0.3.9" 1880 | source = "registry+https://github.com/rust-lang/crates.io-index" 1881 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1882 | dependencies = [ 1883 | "winapi-i686-pc-windows-gnu", 1884 | "winapi-x86_64-pc-windows-gnu", 1885 | ] 1886 | 1887 | [[package]] 1888 | name = "winapi-i686-pc-windows-gnu" 1889 | version = "0.4.0" 1890 | source = "registry+https://github.com/rust-lang/crates.io-index" 1891 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1892 | 1893 | [[package]] 1894 | name = "winapi-x86_64-pc-windows-gnu" 1895 | version = "0.4.0" 1896 | source = "registry+https://github.com/rust-lang/crates.io-index" 1897 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1898 | 1899 | [[package]] 1900 | name = "windows" 1901 | version = "0.44.0" 1902 | source = "registry+https://github.com/rust-lang/crates.io-index" 1903 | checksum = "9e745dab35a0c4c77aa3ce42d595e13d2003d6902d6b08c9ef5fc326d08da12b" 1904 | dependencies = [ 1905 | "windows-targets 0.42.2", 1906 | ] 1907 | 1908 | [[package]] 1909 | name = "windows-link" 1910 | version = "0.1.3" 1911 | source = "registry+https://github.com/rust-lang/crates.io-index" 1912 | checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" 1913 | 1914 | [[package]] 1915 | name = "windows-sys" 1916 | version = "0.48.0" 1917 | source = "registry+https://github.com/rust-lang/crates.io-index" 1918 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 1919 | dependencies = [ 1920 | "windows-targets 0.48.5", 1921 | ] 1922 | 1923 | [[package]] 1924 | name = "windows-sys" 1925 | version = "0.52.0" 1926 | source = "registry+https://github.com/rust-lang/crates.io-index" 1927 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 1928 | dependencies = [ 1929 | "windows-targets 0.52.6", 1930 | ] 1931 | 1932 | [[package]] 1933 | name = "windows-sys" 1934 | version = "0.59.0" 1935 | source = "registry+https://github.com/rust-lang/crates.io-index" 1936 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 1937 | dependencies = [ 1938 | "windows-targets 0.52.6", 1939 | ] 1940 | 1941 | [[package]] 1942 | name = "windows-sys" 1943 | version = "0.60.2" 1944 | source = "registry+https://github.com/rust-lang/crates.io-index" 1945 | checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" 1946 | dependencies = [ 1947 | "windows-targets 0.53.3", 1948 | ] 1949 | 1950 | [[package]] 1951 | name = "windows-targets" 1952 | version = "0.42.2" 1953 | source = "registry+https://github.com/rust-lang/crates.io-index" 1954 | checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" 1955 | dependencies = [ 1956 | "windows_aarch64_gnullvm 0.42.2", 1957 | "windows_aarch64_msvc 0.42.2", 1958 | "windows_i686_gnu 0.42.2", 1959 | "windows_i686_msvc 0.42.2", 1960 | "windows_x86_64_gnu 0.42.2", 1961 | "windows_x86_64_gnullvm 0.42.2", 1962 | "windows_x86_64_msvc 0.42.2", 1963 | ] 1964 | 1965 | [[package]] 1966 | name = "windows-targets" 1967 | version = "0.48.5" 1968 | source = "registry+https://github.com/rust-lang/crates.io-index" 1969 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 1970 | dependencies = [ 1971 | "windows_aarch64_gnullvm 0.48.5", 1972 | "windows_aarch64_msvc 0.48.5", 1973 | "windows_i686_gnu 0.48.5", 1974 | "windows_i686_msvc 0.48.5", 1975 | "windows_x86_64_gnu 0.48.5", 1976 | "windows_x86_64_gnullvm 0.48.5", 1977 | "windows_x86_64_msvc 0.48.5", 1978 | ] 1979 | 1980 | [[package]] 1981 | name = "windows-targets" 1982 | version = "0.52.6" 1983 | source = "registry+https://github.com/rust-lang/crates.io-index" 1984 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 1985 | dependencies = [ 1986 | "windows_aarch64_gnullvm 0.52.6", 1987 | "windows_aarch64_msvc 0.52.6", 1988 | "windows_i686_gnu 0.52.6", 1989 | "windows_i686_gnullvm 0.52.6", 1990 | "windows_i686_msvc 0.52.6", 1991 | "windows_x86_64_gnu 0.52.6", 1992 | "windows_x86_64_gnullvm 0.52.6", 1993 | "windows_x86_64_msvc 0.52.6", 1994 | ] 1995 | 1996 | [[package]] 1997 | name = "windows-targets" 1998 | version = "0.53.3" 1999 | source = "registry+https://github.com/rust-lang/crates.io-index" 2000 | checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" 2001 | dependencies = [ 2002 | "windows-link", 2003 | "windows_aarch64_gnullvm 0.53.0", 2004 | "windows_aarch64_msvc 0.53.0", 2005 | "windows_i686_gnu 0.53.0", 2006 | "windows_i686_gnullvm 0.53.0", 2007 | "windows_i686_msvc 0.53.0", 2008 | "windows_x86_64_gnu 0.53.0", 2009 | "windows_x86_64_gnullvm 0.53.0", 2010 | "windows_x86_64_msvc 0.53.0", 2011 | ] 2012 | 2013 | [[package]] 2014 | name = "windows_aarch64_gnullvm" 2015 | version = "0.42.2" 2016 | source = "registry+https://github.com/rust-lang/crates.io-index" 2017 | checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" 2018 | 2019 | [[package]] 2020 | name = "windows_aarch64_gnullvm" 2021 | version = "0.48.5" 2022 | source = "registry+https://github.com/rust-lang/crates.io-index" 2023 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 2024 | 2025 | [[package]] 2026 | name = "windows_aarch64_gnullvm" 2027 | version = "0.52.6" 2028 | source = "registry+https://github.com/rust-lang/crates.io-index" 2029 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 2030 | 2031 | [[package]] 2032 | name = "windows_aarch64_gnullvm" 2033 | version = "0.53.0" 2034 | source = "registry+https://github.com/rust-lang/crates.io-index" 2035 | checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" 2036 | 2037 | [[package]] 2038 | name = "windows_aarch64_msvc" 2039 | version = "0.42.2" 2040 | source = "registry+https://github.com/rust-lang/crates.io-index" 2041 | checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" 2042 | 2043 | [[package]] 2044 | name = "windows_aarch64_msvc" 2045 | version = "0.48.5" 2046 | source = "registry+https://github.com/rust-lang/crates.io-index" 2047 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 2048 | 2049 | [[package]] 2050 | name = "windows_aarch64_msvc" 2051 | version = "0.52.6" 2052 | source = "registry+https://github.com/rust-lang/crates.io-index" 2053 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 2054 | 2055 | [[package]] 2056 | name = "windows_aarch64_msvc" 2057 | version = "0.53.0" 2058 | source = "registry+https://github.com/rust-lang/crates.io-index" 2059 | checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" 2060 | 2061 | [[package]] 2062 | name = "windows_i686_gnu" 2063 | version = "0.42.2" 2064 | source = "registry+https://github.com/rust-lang/crates.io-index" 2065 | checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" 2066 | 2067 | [[package]] 2068 | name = "windows_i686_gnu" 2069 | version = "0.48.5" 2070 | source = "registry+https://github.com/rust-lang/crates.io-index" 2071 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 2072 | 2073 | [[package]] 2074 | name = "windows_i686_gnu" 2075 | version = "0.52.6" 2076 | source = "registry+https://github.com/rust-lang/crates.io-index" 2077 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 2078 | 2079 | [[package]] 2080 | name = "windows_i686_gnu" 2081 | version = "0.53.0" 2082 | source = "registry+https://github.com/rust-lang/crates.io-index" 2083 | checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" 2084 | 2085 | [[package]] 2086 | name = "windows_i686_gnullvm" 2087 | version = "0.52.6" 2088 | source = "registry+https://github.com/rust-lang/crates.io-index" 2089 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 2090 | 2091 | [[package]] 2092 | name = "windows_i686_gnullvm" 2093 | version = "0.53.0" 2094 | source = "registry+https://github.com/rust-lang/crates.io-index" 2095 | checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" 2096 | 2097 | [[package]] 2098 | name = "windows_i686_msvc" 2099 | version = "0.42.2" 2100 | source = "registry+https://github.com/rust-lang/crates.io-index" 2101 | checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" 2102 | 2103 | [[package]] 2104 | name = "windows_i686_msvc" 2105 | version = "0.48.5" 2106 | source = "registry+https://github.com/rust-lang/crates.io-index" 2107 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 2108 | 2109 | [[package]] 2110 | name = "windows_i686_msvc" 2111 | version = "0.52.6" 2112 | source = "registry+https://github.com/rust-lang/crates.io-index" 2113 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 2114 | 2115 | [[package]] 2116 | name = "windows_i686_msvc" 2117 | version = "0.53.0" 2118 | source = "registry+https://github.com/rust-lang/crates.io-index" 2119 | checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" 2120 | 2121 | [[package]] 2122 | name = "windows_x86_64_gnu" 2123 | version = "0.42.2" 2124 | source = "registry+https://github.com/rust-lang/crates.io-index" 2125 | checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" 2126 | 2127 | [[package]] 2128 | name = "windows_x86_64_gnu" 2129 | version = "0.48.5" 2130 | source = "registry+https://github.com/rust-lang/crates.io-index" 2131 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 2132 | 2133 | [[package]] 2134 | name = "windows_x86_64_gnu" 2135 | version = "0.52.6" 2136 | source = "registry+https://github.com/rust-lang/crates.io-index" 2137 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 2138 | 2139 | [[package]] 2140 | name = "windows_x86_64_gnu" 2141 | version = "0.53.0" 2142 | source = "registry+https://github.com/rust-lang/crates.io-index" 2143 | checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" 2144 | 2145 | [[package]] 2146 | name = "windows_x86_64_gnullvm" 2147 | version = "0.42.2" 2148 | source = "registry+https://github.com/rust-lang/crates.io-index" 2149 | checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" 2150 | 2151 | [[package]] 2152 | name = "windows_x86_64_gnullvm" 2153 | version = "0.48.5" 2154 | source = "registry+https://github.com/rust-lang/crates.io-index" 2155 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 2156 | 2157 | [[package]] 2158 | name = "windows_x86_64_gnullvm" 2159 | version = "0.52.6" 2160 | source = "registry+https://github.com/rust-lang/crates.io-index" 2161 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 2162 | 2163 | [[package]] 2164 | name = "windows_x86_64_gnullvm" 2165 | version = "0.53.0" 2166 | source = "registry+https://github.com/rust-lang/crates.io-index" 2167 | checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" 2168 | 2169 | [[package]] 2170 | name = "windows_x86_64_msvc" 2171 | version = "0.42.2" 2172 | source = "registry+https://github.com/rust-lang/crates.io-index" 2173 | checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" 2174 | 2175 | [[package]] 2176 | name = "windows_x86_64_msvc" 2177 | version = "0.48.5" 2178 | source = "registry+https://github.com/rust-lang/crates.io-index" 2179 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 2180 | 2181 | [[package]] 2182 | name = "windows_x86_64_msvc" 2183 | version = "0.52.6" 2184 | source = "registry+https://github.com/rust-lang/crates.io-index" 2185 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 2186 | 2187 | [[package]] 2188 | name = "windows_x86_64_msvc" 2189 | version = "0.53.0" 2190 | source = "registry+https://github.com/rust-lang/crates.io-index" 2191 | checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" 2192 | 2193 | [[package]] 2194 | name = "winreg" 2195 | version = "0.50.0" 2196 | source = "registry+https://github.com/rust-lang/crates.io-index" 2197 | checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" 2198 | dependencies = [ 2199 | "cfg-if", 2200 | "windows-sys 0.48.0", 2201 | ] 2202 | 2203 | [[package]] 2204 | name = "wit-bindgen-rt" 2205 | version = "0.39.0" 2206 | source = "registry+https://github.com/rust-lang/crates.io-index" 2207 | checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" 2208 | dependencies = [ 2209 | "bitflags 2.9.2", 2210 | ] 2211 | 2212 | [[package]] 2213 | name = "writeable" 2214 | version = "0.6.1" 2215 | source = "registry+https://github.com/rust-lang/crates.io-index" 2216 | checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" 2217 | 2218 | [[package]] 2219 | name = "yoke" 2220 | version = "0.8.0" 2221 | source = "registry+https://github.com/rust-lang/crates.io-index" 2222 | checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" 2223 | dependencies = [ 2224 | "serde", 2225 | "stable_deref_trait", 2226 | "yoke-derive", 2227 | "zerofrom", 2228 | ] 2229 | 2230 | [[package]] 2231 | name = "yoke-derive" 2232 | version = "0.8.0" 2233 | source = "registry+https://github.com/rust-lang/crates.io-index" 2234 | checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" 2235 | dependencies = [ 2236 | "proc-macro2", 2237 | "quote", 2238 | "syn", 2239 | "synstructure", 2240 | ] 2241 | 2242 | [[package]] 2243 | name = "zerocopy" 2244 | version = "0.8.26" 2245 | source = "registry+https://github.com/rust-lang/crates.io-index" 2246 | checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" 2247 | dependencies = [ 2248 | "zerocopy-derive", 2249 | ] 2250 | 2251 | [[package]] 2252 | name = "zerocopy-derive" 2253 | version = "0.8.26" 2254 | source = "registry+https://github.com/rust-lang/crates.io-index" 2255 | checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" 2256 | dependencies = [ 2257 | "proc-macro2", 2258 | "quote", 2259 | "syn", 2260 | ] 2261 | 2262 | [[package]] 2263 | name = "zerofrom" 2264 | version = "0.1.6" 2265 | source = "registry+https://github.com/rust-lang/crates.io-index" 2266 | checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" 2267 | dependencies = [ 2268 | "zerofrom-derive", 2269 | ] 2270 | 2271 | [[package]] 2272 | name = "zerofrom-derive" 2273 | version = "0.1.6" 2274 | source = "registry+https://github.com/rust-lang/crates.io-index" 2275 | checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" 2276 | dependencies = [ 2277 | "proc-macro2", 2278 | "quote", 2279 | "syn", 2280 | "synstructure", 2281 | ] 2282 | 2283 | [[package]] 2284 | name = "zerotrie" 2285 | version = "0.2.2" 2286 | source = "registry+https://github.com/rust-lang/crates.io-index" 2287 | checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" 2288 | dependencies = [ 2289 | "displaydoc", 2290 | "yoke", 2291 | "zerofrom", 2292 | ] 2293 | 2294 | [[package]] 2295 | name = "zerovec" 2296 | version = "0.11.4" 2297 | source = "registry+https://github.com/rust-lang/crates.io-index" 2298 | checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" 2299 | dependencies = [ 2300 | "yoke", 2301 | "zerofrom", 2302 | "zerovec-derive", 2303 | ] 2304 | 2305 | [[package]] 2306 | name = "zerovec-derive" 2307 | version = "0.11.1" 2308 | source = "registry+https://github.com/rust-lang/crates.io-index" 2309 | checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" 2310 | dependencies = [ 2311 | "proc-macro2", 2312 | "quote", 2313 | "syn", 2314 | ] 2315 | --------------------------------------------------------------------------------