├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── dependabot.yml └── workflows │ └── main.yml ├── .gitignore ├── .rustfmt.toml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── _travis.yml ├── analyze ├── Cargo.toml ├── analyses │ ├── diff.rs │ ├── dominators │ │ ├── emit.rs │ │ └── mod.rs │ ├── garbage.rs │ ├── mod.rs │ ├── monos │ │ ├── emit.rs │ │ ├── entry.rs │ │ └── mod.rs │ ├── paths │ │ ├── mod.rs │ │ ├── paths_emit.rs │ │ └── paths_entry.rs │ └── top.rs ├── analyze.rs └── formats │ ├── json.rs │ ├── mod.rs │ └── table.rs ├── guide ├── book.toml └── src │ ├── SUMMARY.md │ ├── chapter_1.md │ ├── concepts │ ├── call-graph.dot │ ├── call-graph.md │ ├── call-graph.svg │ ├── dominator-tree.dot │ ├── dominator-tree.svg │ ├── dominators-and-retained-size.md │ ├── generic-functions-and-monomorphization.md │ ├── index.md │ └── paths.md │ ├── contributing.md │ ├── contributing │ ├── building.md │ ├── code-formatting.md │ ├── code-of-conduct.md │ ├── index.md │ ├── pull-requests.md │ ├── team.md │ └── testing.md │ ├── index.md │ ├── install.md │ ├── supported-binary-formats.md │ ├── theme │ ├── book.js │ ├── css │ │ ├── chrome.css │ │ ├── general.css │ │ ├── print.css │ │ └── variables.css │ ├── favicon.png │ ├── highlight.css │ ├── highlight.js │ └── index.hbs │ ├── twiggy.png │ ├── twiggy.svg │ └── usage │ ├── as-a-crate.md │ ├── command-line-interface │ ├── diff.md │ ├── dominators.md │ ├── garbage.md │ ├── index.md │ ├── monos.md │ ├── paths.md │ └── top.md │ ├── index.md │ └── on-the-web-with-webassembly.md ├── ir ├── Cargo.toml ├── graph_impl.rs └── ir.rs ├── job_runner ├── Cargo.toml └── src │ └── main.rs ├── opt ├── Cargo.toml ├── build.rs ├── definitions.rs └── opt.rs ├── parser ├── Cargo.toml ├── object_parse │ └── mod.rs ├── parser.rs └── wasm_parse │ └── mod.rs ├── publish.sh ├── releases ├── friends.sh └── release-announcement-template.md ├── traits ├── Cargo.toml └── traits.rs ├── twiggy ├── Cargo.toml ├── tests │ └── all │ │ ├── diff_tests.rs │ │ ├── dominators_tests.rs │ │ ├── elf_format_tests.rs │ │ ├── expectations │ │ ├── cpp_monos │ │ ├── diff_test_exact_wee_alloc │ │ ├── diff_test_regex_wee_alloc │ │ ├── diff_wee_alloc │ │ ├── diff_wee_alloc_all │ │ ├── diff_wee_alloc_all_json │ │ ├── diff_wee_alloc_csv │ │ ├── diff_wee_alloc_csv_top_5 │ │ ├── diff_wee_alloc_json │ │ ├── diff_wee_alloc_json_top_5 │ │ ├── diff_wee_alloc_top_5 │ │ ├── dominators_csv_does_not_summarize_garbage_if_all_items_are_reachable │ │ ├── dominators_json_prints_multiple_root_items │ │ ├── dominators_regex_any_func │ │ ├── dominators_summarizes_unreachable_items │ │ ├── dominators_wee_alloc │ │ ├── dominators_wee_alloc_csv │ │ ├── dominators_wee_alloc_json │ │ ├── dominators_wee_alloc_subtree │ │ ├── dominators_wee_alloc_subtree_json │ │ ├── dominators_wee_alloc_with_depth_and_row │ │ ├── elf_dominators │ │ ├── elf_dominators2 │ │ ├── elf_dominators3 │ │ ├── elf_paths │ │ ├── elf_paths2 │ │ ├── elf_top_25_hello_world_rs │ │ ├── elf_top_hello_world_rs │ │ ├── garbage │ │ ├── garbage_json │ │ ├── garbage_top_2 │ │ ├── garbage_top_2_json │ │ ├── garbage_wee_alloc_all │ │ ├── garbage_wee_alloc_all_json │ │ ├── garbage_wee_alloc_show_data_segments │ │ ├── garbage_wee_alloc_show_data_segments_json │ │ ├── garbage_wee_alloc_top_10 │ │ ├── garbage_wee_alloc_top_10_json │ │ ├── garbage_wee_alloc_top_2_show_data_segments │ │ ├── garbage_wee_alloc_top_2_show_data_segments_json │ │ ├── issue_16 │ │ ├── monos │ │ ├── monos_all │ │ ├── monos_all_generics │ │ ├── monos_all_monos │ │ ├── monos_functions │ │ ├── monos_json │ │ ├── monos_maxes │ │ ├── monos_only_all_generics │ │ ├── monos_only_generics │ │ ├── monos_regex │ │ ├── monos_wasm_csv │ │ ├── output_to_file │ │ ├── paths_error_test_no_max_paths │ │ ├── paths_error_test_no_max_paths_csv │ │ ├── paths_error_test_no_max_paths_json │ │ ├── paths_error_test_one_path │ │ ├── paths_error_test_one_path_csv │ │ ├── paths_error_test_one_path_json │ │ ├── paths_test_called_once │ │ ├── paths_test_called_once_csv │ │ ├── paths_test_called_once_json │ │ ├── paths_test_called_twice │ │ ├── paths_test_called_twice_csv │ │ ├── paths_test_called_twice_json │ │ ├── paths_test_default_output │ │ ├── paths_test_default_output_csv │ │ ├── paths_test_default_output_desc │ │ ├── paths_test_default_output_desc_with_depth │ │ ├── paths_test_default_output_json │ │ ├── paths_test_regex_called_any │ │ ├── paths_test_regex_exports │ │ ├── paths_test_regex_exports_desc │ │ ├── paths_wee_alloc │ │ ├── paths_wee_alloc_csv │ │ ├── paths_wee_alloc_json │ │ ├── paths_wee_alloc_with_depth_and_paths │ │ ├── paths_wee_alloc_with_depth_and_paths_csv │ │ ├── paths_wee_alloc_with_depth_and_paths_json │ │ ├── top_2_csv │ │ ├── top_2_csv_retained │ │ ├── top_2_json │ │ ├── top_2_json_retained │ │ ├── top_mappings │ │ ├── top_memory_module │ │ ├── top_mono │ │ ├── top_retained_mappings │ │ ├── top_retained_wee_alloc │ │ └── top_wee_alloc │ │ ├── fixtures │ │ ├── cpp-monos.cpp │ │ ├── cpp-monos.wasm │ │ ├── garbage.wasm │ │ ├── garbage.wat │ │ ├── hello_elf │ │ ├── hello_world.rs │ │ ├── mappings.wasm │ │ ├── memory.wasm │ │ ├── memory.wat │ │ ├── mono.wasm │ │ ├── monos.rs │ │ ├── monos.wasm │ │ ├── paths_test.wasm │ │ ├── paths_test.wat │ │ ├── wee_alloc.2.wasm │ │ └── wee_alloc.wasm │ │ ├── garbage_tests.rs │ │ ├── main.rs │ │ ├── monos_tests.rs │ │ ├── paths_tests.rs │ │ └── top_tests.rs └── twiggy.rs └── wasm-api ├── Cargo.toml └── wasm-api.rs /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 🐛 3 | about: Something not working as expected? 4 | --- 5 | 6 | ## 🐛 Bug Description 7 | 8 | Describe your issue in detail. 9 | 10 | twiggy version: (replace this with the output of `twiggy --version`) 11 | 12 | #### 🌍 Test Case 13 | 14 | Upload the test case and link to it here. For example, a `.wasm` file that 15 | Twiggy fails to parse. 16 | 17 | #### 👟 Steps to Reproduce 18 | 19 | Precise steps describing how to reproduce the issue, including commands and 20 | flags run. For example: 21 | 22 | * Run `twiggy top -n 25 test_case.wasm` 23 | * ... 24 | 25 | #### 😲 Actual Behavior 26 | 27 | What happens after you follow the steps to reproduce? Include console output, 28 | error messages, stack traces, etc. 29 | 30 | #### 🤔 Expected Behavior 31 | 32 | What should have happened instead? 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 💡 3 | about: Suggest a new feature for Twiggy 4 | --- 5 | 6 | ## 💡 Feature Description 7 | 8 | Explanation of the requested feature. What use case does it solve? 9 | 10 | #### 💻 Example Usage 11 | 12 | Include a strawman example of how a user might use this new feature, and what 13 | the output would look like. 14 | 15 | #### 🙌 Are you interested in implementing this feature? 16 | 17 | Add an "X" to one of the following: 18 | 19 | * [ ] Yes 20 | * [ ] Yes if I had a mentor to help me 21 | * [ ] No 22 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "08:00" 8 | open-pull-requests-limit: 10 9 | ignore: 10 | - dependency-name: wasmparser 11 | versions: 12 | - 0.73.0 13 | - 0.73.1 14 | - 0.74.0 15 | - 0.75.0 16 | - 0.76.0 17 | - 0.77.0 18 | - dependency-name: wasm-bindgen 19 | versions: 20 | - 0.2.70 21 | - 0.2.71 22 | - 0.2.72 23 | - dependency-name: regex 24 | versions: 25 | - 1.4.3 26 | - 1.4.4 27 | - 1.4.5 28 | - dependency-name: serde_derive 29 | versions: 30 | - 1.0.123 31 | - 1.0.124 32 | - dependency-name: serde 33 | versions: 34 | - 1.0.123 35 | - 1.0.124 36 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | lints: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v2 17 | - uses: actions-rs/toolchain@v1 18 | with: 19 | toolchain: stable 20 | components: rustfmt, clippy 21 | - uses: actions-rs/cargo@v1 22 | with: 23 | command: fmt 24 | args: --all -- --check 25 | - uses: actions-rs/cargo@v1 26 | with: 27 | command: clippy 28 | test: 29 | strategy: 30 | matrix: 31 | os: ['ubuntu-latest', 'windows-latest', 'macos-latest'] 32 | runs-on: ${{ matrix.os }} 33 | steps: 34 | - uses: actions/checkout@v2 35 | - uses: actions-rs/toolchain@v1 36 | with: 37 | toolchain: stable 38 | - name: Test 39 | run: cargo test --all --exclude twiggy-wasm-api 40 | wasm: 41 | runs-on: ubuntu-latest 42 | steps: 43 | - uses: actions/checkout@v2 44 | - uses: actions-rs/toolchain@v1 45 | with: 46 | toolchain: stable 47 | - name: Run test script with WASM job 48 | run: cargo run --bin job_runner -- --wasm 49 | # docs_deploy: 50 | # runs-on: ubuntu-latest 51 | # needs: test 52 | # steps: 53 | # - name: Checkout 54 | # uses: actions/checkout@v2.3.1 55 | # - name: Build the book 56 | # run: cd guide && mdbook build 57 | # - name: Deploy 58 | # if: github.event_name == 'push' && github.ref == 'refs/heads/main' 59 | # uses: JamesIves/github-pages-deploy-action@4.1.4 60 | # with: 61 | # branch: master 62 | # folder: guide/book 63 | # token: ${{ secrets.GITHUB_TOKEN }} 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | **/*.rs.bk 3 | Cargo.lock 4 | twiggy/tests/all/whatever-output.txt 5 | wasm-api/.crates.toml 6 | wasm-api/bin 7 | twiggy_wasm_api.d.ts 8 | twiggy_wasm_api.js 9 | twiggy_wasm_api.wasm 10 | twiggy_wasm_api_bg.wasm 11 | twiggy_wasm_api_bg.d.ts 12 | guide/book 13 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | # We do not merge derives because of `twiggy-opt` and its build script. 2 | # Merging derives in `opt/definitions` will break the wasm api. 3 | merge_derives = false 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to `twiggy` 2 | 3 | [Read the "Contributing to Twiggy" section of the Twiggy guide!](https://rustwasm.github.io/twiggy/contributing/index.html) 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "./analyze", 4 | "./ir", 5 | "./opt", 6 | "./parser", 7 | "./twiggy", 8 | "./traits", 9 | "./wasm-api", 10 | "./job_runner" 11 | ] 12 | 13 | [profile.release] 14 | codegen-units = 1 15 | debug = true 16 | incremental = false 17 | lto = true 18 | opt-level = "s" 19 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 The Rust Project Developers 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |

Twiggy🌱

4 | 5 | A code size profiler for Wasm 6 | 7 |

8 | 9 | 10 | 11 | 12 |

13 | 14 |

15 | Guide 16 | | 17 | Contributing 18 | | 19 | Chat 20 |

21 | 22 | Built with 🦀🕸 by The Rust and WebAssembly Working Group 23 |
24 | 25 | ## About 26 | 27 | Twiggy is a code size profiler for Wasm. It analyzes a binary's call graph to 28 | answer questions like: 29 | 30 | * Why was this function included in the binary in the first place? Who calls it? 31 | 32 | * What is the *retained size* of this function? I.e. how much space would be 33 | saved if I removed it and all the functions that become dead code after its 34 | removal. 35 | 36 | Use Twiggy to make your binaries slim! 37 | 38 | ## Install Twiggy 39 | 40 | Ensure that you have [the Rust toolchain installed](https://www.rust-lang.org/), 41 | then run: 42 | 43 | ``` 44 | cargo install twiggy 45 | ``` 46 | 47 | ## Learn More! 48 | 49 | [**Read the Twiggy guide!**](https://rustwasm.github.io/twiggy) 50 | 51 |
52 | 53 |
54 | -------------------------------------------------------------------------------- /_travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | cache: cargo 3 | 4 | rust: 5 | - stable 6 | - beta 7 | - nightly 8 | 9 | env: 10 | matrix: 11 | - JOB="test" 12 | 13 | matrix: 14 | fast_finish: true 15 | include: 16 | - rust: nightly 17 | env: JOB="wasm" 18 | # Build and deploy the mdbook guide. 19 | - rust: stable 20 | env: JOB="guide" 21 | before_script: 22 | - (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update) 23 | - (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.2" mdbook) 24 | - cargo install-update -a 25 | script: 26 | - cd guide && mdbook build 27 | deploy: 28 | provider: pages 29 | skip-cleanup: true 30 | github-token: $GITHUB_TOKEN 31 | local-dir: guide/book 32 | keep-history: false 33 | on: 34 | branch: master 35 | - rust: stable 36 | env: JOB="format" 37 | before_script: 38 | - rustup component add rustfmt --toolchain stable 39 | script: 40 | - cargo fmt -- --check 41 | - rust: stable 42 | env: JOB="lint" 43 | before_script: 44 | - rustup component add clippy --toolchain stable 45 | script: 46 | - cargo clippy -- -D warnings 47 | allow_failures: 48 | - rust: stable 49 | env: JOB="lint" 50 | 51 | script: 52 | - ./ci/script.sh 53 | -------------------------------------------------------------------------------- /analyze/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Nick Fitzgerald "] 3 | categories = [] 4 | description = "Analyses for the Twiggy code size profiler." 5 | license = "Apache-2.0/MIT" 6 | name = "twiggy-analyze" 7 | readme = "../README.md" 8 | repository = "https://github.com/rustwasm/twiggy" 9 | version = "0.7.0" 10 | edition = "2018" 11 | 12 | [lib] 13 | path = "./analyze.rs" 14 | 15 | [dependencies] 16 | anyhow = "1.0" 17 | twiggy-ir = { version = "=0.7.0", path = "../ir" } 18 | twiggy-opt = { version = "=0.7.0", path = "../opt", default-features = false } 19 | twiggy-traits = { version = "=0.7.0", path = "../traits" } 20 | csv = "1.2.2" 21 | regex = "1.4.2" 22 | serde = "1.0" 23 | serde_derive = "1.0" 24 | petgraph = "0.6.2" 25 | 26 | [features] 27 | default = ["emit_csv", "emit_json", "emit_text"] 28 | emit_json = ["twiggy-traits/emit_json"] 29 | emit_csv = ["twiggy-traits/emit_csv"] 30 | emit_text = ["twiggy-traits/emit_text"] 31 | -------------------------------------------------------------------------------- /analyze/analyses/dominators/mod.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | 3 | use regex; 4 | 5 | use twiggy_ir as ir; 6 | use twiggy_opt as opt; 7 | use twiggy_traits as traits; 8 | 9 | use crate::analyses::garbage; 10 | 11 | mod emit; 12 | 13 | struct DominatorTree { 14 | tree: BTreeMap>, 15 | items: Vec, 16 | opts: opt::Dominators, 17 | unreachable_items_summary: Option, 18 | } 19 | 20 | struct UnreachableItemsSummary { 21 | count: usize, 22 | size: u32, 23 | size_percent: f64, 24 | } 25 | 26 | /// Compute the dominator tree for the given IR graph. 27 | pub fn dominators( 28 | items: &mut ir::Items, 29 | opts: &opt::Dominators, 30 | ) -> anyhow::Result> { 31 | items.compute_dominator_tree(); 32 | items.compute_dominators(); 33 | items.compute_retained_sizes(); 34 | items.compute_predecessors(); 35 | 36 | let arguments = opts.items(); 37 | let dominator_items = if arguments.is_empty() { 38 | vec![items.meta_root()] 39 | } else if opts.using_regexps() { 40 | let regexps = regex::RegexSet::new(arguments)?; 41 | let mut sorted_items: Vec<_> = items 42 | .iter() 43 | .filter(|item| regexps.is_match(&item.name())) 44 | .map(|item| item.id()) 45 | .collect(); 46 | sorted_items.sort_by_key(|id| -i64::from(items.retained_size(*id))); 47 | sorted_items 48 | } else { 49 | arguments 50 | .iter() 51 | .filter_map(|name| items.get_item_by_name(name)) 52 | .map(|item| item.id()) 53 | .collect() 54 | }; 55 | 56 | let tree = DominatorTree { 57 | tree: items.dominator_tree().clone(), 58 | items: dominator_items, 59 | opts: opts.clone(), 60 | unreachable_items_summary: summarize_unreachable_items(items, opts), 61 | }; 62 | 63 | Ok(Box::new(tree) as Box<_>) 64 | } 65 | 66 | fn summarize_unreachable_items( 67 | items: &mut ir::Items, 68 | opts: &opt::Dominators, 69 | ) -> Option { 70 | let (size, count) = garbage::get_unreachable_items(&items) 71 | .map(|item| item.size()) 72 | .fold((0, 0), |(s, c), curr| (s + curr, c + 1)); 73 | if opts.items().is_empty() && size > 0 { 74 | Some(UnreachableItemsSummary { 75 | count, 76 | size, 77 | size_percent: (f64::from(size)) / (f64::from(items.size())) * 100.0, 78 | }) 79 | } else { 80 | None 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /analyze/analyses/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod diff; 2 | pub mod dominators; 3 | pub mod garbage; 4 | pub mod monos; 5 | pub mod paths; 6 | pub mod top; 7 | -------------------------------------------------------------------------------- /analyze/analyses/monos/entry.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, PartialEq, Eq)] 2 | pub(super) struct MonosEntry { 3 | pub name: String, 4 | pub insts: Vec<(String, u32)>, 5 | pub size: u32, 6 | pub bloat: u32, 7 | } 8 | 9 | impl PartialOrd for MonosEntry { 10 | fn partial_cmp(&self, rhs: &MonosEntry) -> Option { 11 | Some(self.cmp(rhs)) 12 | } 13 | } 14 | 15 | impl Ord for MonosEntry { 16 | fn cmp(&self, rhs: &MonosEntry) -> std::cmp::Ordering { 17 | rhs.bloat 18 | .cmp(&self.bloat) 19 | .then(rhs.size.cmp(&self.size)) 20 | .then(self.insts.cmp(&rhs.insts)) 21 | .then(self.name.cmp(&rhs.name)) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /analyze/analyses/paths/mod.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeSet; 2 | 3 | use regex; 4 | 5 | use twiggy_ir as ir; 6 | use twiggy_opt as opt; 7 | use twiggy_traits as traits; 8 | 9 | mod paths_emit; 10 | mod paths_entry; 11 | 12 | use self::paths_entry::PathsEntry; 13 | 14 | #[derive(Debug)] 15 | struct Paths { 16 | opts: opt::Paths, 17 | entries: Vec, 18 | } 19 | 20 | /// Find all retaining paths for the given items. 21 | pub fn paths(items: &mut ir::Items, opts: &opt::Paths) -> anyhow::Result> { 22 | // The predecessor tree only needs to be computed if we are ascending 23 | // through the retaining paths. 24 | if !opts.descending() { 25 | items.compute_predecessors(); 26 | } 27 | 28 | // Initialize the collection of Id values whose retaining paths we will emit. 29 | let opts = opts.clone(); 30 | let entries = get_starting_positions(items, &opts)? 31 | .iter() 32 | .map(|id| create_entry(*id, items, &opts, &mut BTreeSet::new())) 33 | .collect(); 34 | 35 | let paths = Paths { opts, entries }; 36 | 37 | Ok(Box::new(paths) as Box<_>) 38 | } 39 | 40 | /// This helper function is used to collect the `ir::Id` values for the top-most 41 | /// path entries for the `Paths` object, based on the given options. 42 | fn get_starting_positions(items: &ir::Items, opts: &opt::Paths) -> anyhow::Result> { 43 | // Collect Id's if no arguments are given and we are ascending the retaining paths. 44 | let get_functions_default = || -> Vec { 45 | let mut sorted_items = items 46 | .iter() 47 | .filter(|item| item.id() != items.meta_root()) 48 | .collect::>(); 49 | sorted_items.sort_by(|a, b| b.size().cmp(&a.size())); 50 | sorted_items.iter().map(|item| item.id()).collect() 51 | }; 52 | 53 | // Collect Id's if no arguments are given and we are descending the retaining paths. 54 | let get_functions_default_desc = || -> Vec { 55 | let mut roots = items 56 | .neighbors(items.meta_root()) 57 | .map(|id| &items[id]) 58 | .collect::>(); 59 | roots.sort_by(|a, b| b.size().cmp(&a.size())); 60 | roots.into_iter().map(|item| item.id()).collect() 61 | }; 62 | 63 | // Collect Id's if arguments were given that should be used as regular expressions. 64 | let get_regexp_matches = || -> anyhow::Result> { 65 | let regexps = regex::RegexSet::new(opts.functions())?; 66 | let matches = items 67 | .iter() 68 | .filter(|item| regexps.is_match(item.name())) 69 | .map(|item| item.id()) 70 | .collect(); 71 | Ok(matches) 72 | }; 73 | 74 | // Collect Id's if arguments were given that should be used as exact names. 75 | let get_exact_matches = || -> Vec { 76 | opts.functions() 77 | .iter() 78 | .filter_map(|s| items.get_item_by_name(s)) 79 | .map(|item| item.id()) 80 | .collect() 81 | }; 82 | 83 | // Collect the starting positions based on the relevant options given. 84 | // If arguments were given, search for matches depending on whether or 85 | // not these should be treated as regular expressions. Otherwise, collect 86 | // the starting positions based on the direction we will be traversing. 87 | let args_given = !opts.functions().is_empty(); 88 | let using_regexps = opts.using_regexps(); 89 | let descending = opts.descending(); 90 | let res = match (args_given, using_regexps, descending) { 91 | (true, true, _) => get_regexp_matches()?, 92 | (true, false, _) => get_exact_matches(), 93 | (false, _, true) => get_functions_default_desc(), 94 | (false, _, false) => get_functions_default(), 95 | }; 96 | 97 | Ok(res) 98 | } 99 | 100 | /// Create a `PathsEntry` object for the given item. 101 | fn create_entry( 102 | id: ir::Id, 103 | items: &ir::Items, 104 | opts: &opt::Paths, 105 | seen: &mut BTreeSet, 106 | ) -> PathsEntry { 107 | // Determine the item's name and size. 108 | let item = &items[id]; 109 | let name = item.name().to_string(); 110 | let size = item.size(); 111 | 112 | // Collect the `ir::Id` values of this entry's children, depending on 113 | // whether we are ascending or descending the IR-tree. 114 | let children_ids: Vec = if opts.descending() { 115 | items 116 | .neighbors(id) 117 | .map(|id| id as ir::Id) 118 | .filter(|id| !seen.contains(id)) 119 | .filter(|&id| id != items.meta_root()) 120 | .collect() 121 | } else { 122 | items 123 | .predecessors(id) 124 | .map(|id| id as ir::Id) 125 | .filter(|id| !seen.contains(id)) 126 | .filter(|&id| id != items.meta_root()) 127 | .collect() 128 | }; 129 | 130 | // Temporarily add the current item to the set of discovered nodes, and 131 | // create an entry for each child. Collect these into a `children` vector. 132 | seen.insert(id); 133 | let children = children_ids 134 | .into_iter() 135 | .map(|id| create_entry(id, items, opts, seen)) 136 | .collect(); 137 | seen.remove(&id); 138 | 139 | PathsEntry { 140 | name, 141 | size, 142 | children, 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /analyze/analyses/paths/paths_entry.rs: -------------------------------------------------------------------------------- 1 | use std::cmp; 2 | 3 | #[derive(Debug, PartialEq, Eq)] 4 | pub(super) struct PathsEntry { 5 | pub name: String, 6 | pub size: u32, 7 | pub children: Vec, 8 | } 9 | 10 | impl PathsEntry { 11 | pub fn _count(&self) -> u32 { 12 | 1 + self.children.iter().map(|c| c._count()).sum::() 13 | } 14 | } 15 | 16 | impl PartialOrd for PathsEntry { 17 | fn partial_cmp(&self, rhs: &Self) -> Option { 18 | Some(self.cmp(rhs)) 19 | } 20 | } 21 | 22 | impl Ord for PathsEntry { 23 | fn cmp(&self, rhs: &Self) -> cmp::Ordering { 24 | rhs.size.cmp(&self.size).then(self.name.cmp(&rhs.name)) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /analyze/analyze.rs: -------------------------------------------------------------------------------- 1 | //! Implementations of the analyses that `twiggy` runs on its IR. 2 | 3 | #![deny(missing_docs)] 4 | #![deny(missing_debug_implementations)] 5 | 6 | mod analyses; 7 | mod formats; 8 | 9 | pub use analyses::{ 10 | diff::diff, dominators::dominators, garbage::garbage, monos::monos, paths::paths, top::top, 11 | }; 12 | -------------------------------------------------------------------------------- /analyze/formats/json.rs: -------------------------------------------------------------------------------- 1 | // A couple methods are dead, but removing them would make the API oddly 2 | // imbalanced and we might want to use them in some future analysis. 3 | #![allow(dead_code)] 4 | 5 | use std::io; 6 | 7 | pub trait JsonPrimitive { 8 | fn json_primitive(&self, w: &mut dyn io::Write) -> io::Result<()>; 9 | } 10 | 11 | impl<'a> JsonPrimitive for &'a str { 12 | fn json_primitive(&self, w: &mut dyn io::Write) -> io::Result<()> { 13 | write!(w, "\"")?; 14 | for c in self.chars() { 15 | match c { 16 | '"' => write!(w, "\\\"")?, 17 | '\\' => write!(w, "\\")?, 18 | '\n' => writeln!(w,)?, 19 | c => write!(w, "{}", c)?, 20 | } 21 | } 22 | write!(w, "\"") 23 | } 24 | } 25 | 26 | impl JsonPrimitive for f64 { 27 | fn json_primitive(&self, w: &mut dyn io::Write) -> io::Result<()> { 28 | write!(w, "{}", self) 29 | } 30 | } 31 | 32 | impl JsonPrimitive for u32 { 33 | fn json_primitive(&self, w: &mut dyn io::Write) -> io::Result<()> { 34 | write!(w, "{}", self) 35 | } 36 | } 37 | 38 | pub fn array(w: &mut dyn io::Write) -> io::Result { 39 | write!(w, "[")?; 40 | Ok(Array { 41 | w, 42 | need_comma: false, 43 | }) 44 | } 45 | 46 | pub fn object(w: &mut dyn io::Write) -> io::Result { 47 | write!(w, "{{")?; 48 | Ok(Object { 49 | w, 50 | need_comma: false, 51 | }) 52 | } 53 | 54 | pub struct Array<'a> { 55 | w: &'a mut dyn io::Write, 56 | need_comma: bool, 57 | } 58 | 59 | impl<'a> Drop for Array<'a> { 60 | fn drop(&mut self) { 61 | let _ = write!(self.w, "]"); 62 | } 63 | } 64 | 65 | impl<'a> Array<'a> { 66 | fn comma(&mut self) -> io::Result<()> { 67 | if self.need_comma { 68 | write!(self.w, ",")?; 69 | } 70 | self.need_comma = true; 71 | Ok(()) 72 | } 73 | 74 | pub fn object(&mut self) -> io::Result { 75 | self.comma()?; 76 | object(&mut *self.w) 77 | } 78 | 79 | pub fn array(&mut self) -> io::Result { 80 | self.comma()?; 81 | array(&mut *self.w) 82 | } 83 | 84 | pub fn elem

(&mut self, elem: P) -> io::Result<()> 85 | where 86 | P: JsonPrimitive, 87 | { 88 | self.comma()?; 89 | elem.json_primitive(self.w) 90 | } 91 | } 92 | 93 | pub struct Object<'a> { 94 | w: &'a mut dyn io::Write, 95 | need_comma: bool, 96 | } 97 | 98 | impl<'a> Drop for Object<'a> { 99 | fn drop(&mut self) { 100 | let _ = write!(self.w, "}}"); 101 | } 102 | } 103 | 104 | impl<'a> Object<'a> { 105 | fn comma_and_name(&mut self, name: S) -> io::Result<()> 106 | where 107 | S: AsRef, 108 | { 109 | if self.need_comma { 110 | write!(self.w, ",")?; 111 | } 112 | self.need_comma = true; 113 | name.as_ref().json_primitive(self.w)?; 114 | write!(self.w, ":") 115 | } 116 | 117 | pub fn object(&mut self, name: S) -> io::Result 118 | where 119 | S: AsRef, 120 | { 121 | self.comma_and_name(name)?; 122 | object(&mut *self.w) 123 | } 124 | 125 | pub fn array(&mut self, name: S) -> io::Result 126 | where 127 | S: AsRef, 128 | { 129 | self.comma_and_name(name)?; 130 | array(&mut *self.w) 131 | } 132 | 133 | pub fn field(&mut self, name: S, val: P) -> io::Result<()> 134 | where 135 | S: AsRef, 136 | P: JsonPrimitive, 137 | { 138 | self.comma_and_name(name)?; 139 | val.json_primitive(self.w) 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /analyze/formats/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod json; 2 | pub mod table; 3 | -------------------------------------------------------------------------------- /analyze/formats/table.rs: -------------------------------------------------------------------------------- 1 | use std::cmp; 2 | use std::fmt; 3 | 4 | #[derive(Debug, Clone, Copy)] 5 | pub enum Align { 6 | Left, 7 | Right, 8 | } 9 | 10 | #[derive(Debug, Clone)] 11 | pub struct Table { 12 | header: Vec<(Align, String)>, 13 | rows: Vec>, 14 | } 15 | 16 | impl Table { 17 | pub(crate) fn with_header(header: Vec<(Align, String)>) -> Table { 18 | assert!(!header.is_empty()); 19 | Table { 20 | header, 21 | rows: vec![], 22 | } 23 | } 24 | 25 | pub(crate) fn add_row(&mut self, row: Vec) { 26 | assert_eq!(self.header.len(), row.len()); 27 | self.rows.push(row); 28 | } 29 | } 30 | 31 | impl fmt::Display for Table { 32 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 33 | let mut maxs: Vec<_> = self.header.iter().map(|h| h.1.len()).collect(); 34 | 35 | for row in &self.rows { 36 | for (i, x) in row.iter().enumerate() { 37 | maxs[i] = cmp::max(maxs[i], x.len()); 38 | } 39 | } 40 | 41 | let last = self.header.len() - 1; 42 | 43 | for (i, h) in self.header.iter().map(|h| &h.1).enumerate() { 44 | if i == 0 { 45 | write!(f, " ")?; 46 | } else { 47 | write!(f, " │ ")?; 48 | } 49 | 50 | write!(f, "{}", h)?; 51 | if i != last { 52 | for _ in 0..maxs[i] - h.len() { 53 | write!(f, " ")?; 54 | } 55 | } 56 | } 57 | writeln!(f)?; 58 | 59 | for (i, max_len) in maxs.iter().enumerate().take(self.header.len()) { 60 | if i == 0 { 61 | write!(f, "─")?; 62 | } else { 63 | write!(f, "─┼─")?; 64 | } 65 | for _ in 0..*max_len { 66 | write!(f, "─")?; 67 | } 68 | } 69 | writeln!(f)?; 70 | 71 | for row in &self.rows { 72 | for (i, (x, align)) in row.iter().zip(self.header.iter().map(|h| h.0)).enumerate() { 73 | if i == 0 { 74 | write!(f, " ")?; 75 | } else { 76 | write!(f, " ┊ ")?; 77 | } 78 | 79 | match align { 80 | Align::Left => { 81 | write!(f, "{}", x)?; 82 | if i != last { 83 | for _ in 0..maxs[i] - x.len() { 84 | write!(f, " ")?; 85 | } 86 | } 87 | } 88 | Align::Right => { 89 | for _ in 0..maxs[i] - x.len() { 90 | write!(f, " ")?; 91 | } 92 | write!(f, "{}", x)?; 93 | } 94 | } 95 | } 96 | writeln!(f)?; 97 | } 98 | 99 | Ok(()) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /guide/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["Nick Fitzgerald"] 3 | multilingual = false 4 | src = "src" 5 | title = "Twiggy🌱" 6 | [output.html] 7 | theme = "src/theme" 8 | -------------------------------------------------------------------------------- /guide/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | [Introduction](./index.md) 4 | 5 | -------------------------------------------------------------------------------- 6 | 7 | - [📦 Install](./install.md) 8 | - [💡 Concepts](./concepts/index.md) 9 | - [Call Graph](./concepts/call-graph.md) 10 | - [Paths](./concepts/paths.md) 11 | - [Dominators and Retained Size](./concepts/dominators-and-retained-size.md) 12 | - [Generic Functions and Monomorphization](./concepts/generic-functions-and-monomorphization.md) 13 | - [🏋️‍♀️ Usage](./usage/index.md) 14 | - [⌨ Command Line Interface](./usage/command-line-interface/index.md) 15 | - [`twiggy top`](./usage/command-line-interface/top.md) 16 | - [`twiggy paths`](./usage/command-line-interface/paths.md) 17 | - [`twiggy monos`](./usage/command-line-interface/monos.md) 18 | - [`twiggy dominators`](./usage/command-line-interface/dominators.md) 19 | - [`twiggy diff`](./usage/command-line-interface/diff.md) 20 | - [`twiggy garbage`](./usage/command-line-interface/garbage.md) 21 | - [🦀 As a Crate](./usage/as-a-crate.md) 22 | - [🕸 On the Web with WebAssembly](./usage/on-the-web-with-webassembly.md) 23 | - [🔎 Supported Binary Formats](./supported-binary-formats.md) 24 | 25 | -------------------------------------------------------------------------------- 26 | 27 | - [🙌 Contributing to Twiggy](./contributing/index.md) 28 | - [Building](./contributing/building.md) 29 | - [Testing](./contributing/testing.md) 30 | - [Code Formatting](./contributing/code-formatting.md) 31 | - [Pull Requests](./contributing/pull-requests.md) 32 | - [Team](./contributing/team.md) 33 | -------------------------------------------------------------------------------- /guide/src/chapter_1.md: -------------------------------------------------------------------------------- 1 | # Chapter 1 2 | -------------------------------------------------------------------------------- /guide/src/concepts/call-graph.dot: -------------------------------------------------------------------------------- 1 | digraph "call-graph" { 2 | "shred" -> "gnar_gnar"; 3 | "shred" -> "bluebird"; 4 | 5 | "gnar_gnar" -> "weather_report"; 6 | "gnar_gnar" -> "pow"; 7 | 8 | "bluebird" -> "weather_report"; 9 | 10 | "weather_report" -> "shred"; 11 | 12 | "pow" -> "fluffy"; 13 | "pow" -> "soft"; 14 | 15 | "baker" -> "hood"; 16 | } 17 | -------------------------------------------------------------------------------- /guide/src/concepts/call-graph.md: -------------------------------------------------------------------------------- 1 | # Call Graph 2 | 3 | Consider the following functions: 4 | 5 | ```rust 6 | pub fn shred() { 7 | gnar_gnar(); 8 | bluebird(); 9 | } 10 | 11 | fn gnar_gnar() { 12 | weather_report(); 13 | pow(); 14 | } 15 | 16 | fn bluebird() { 17 | weather_report(); 18 | } 19 | 20 | fn weather_report() { 21 | shred(); 22 | } 23 | 24 | fn pow() { 25 | fluffy(); 26 | soft(); 27 | } 28 | 29 | fn fluffy() {} 30 | 31 | fn soft() {} 32 | 33 | pub fn baker() { 34 | hood(); 35 | } 36 | 37 | fn hood() {} 38 | ``` 39 | 40 | If we treat every function as a *vertex* in a graph, and if we add an *edge* 41 | from *A* to *B* if function *A* calls function *B*, then we get the following 42 | *call graph*: 43 | 44 | [Call Graph](./call-graph.svg) 45 | -------------------------------------------------------------------------------- /guide/src/concepts/dominator-tree.dot: -------------------------------------------------------------------------------- 1 | digraph "dominator-tree" { 2 | "shred" -> "gnar_gnar"; 3 | "shred" -> "bluebird"; 4 | "shred" -> "weather_report"; 5 | 6 | "gnar_gnar" -> "pow"; 7 | 8 | "pow" -> "fluffy"; 9 | "pow" -> "soft"; 10 | } 11 | -------------------------------------------------------------------------------- /guide/src/concepts/dominator-tree.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | dominator-tree 11 | 12 | 13 | shred 14 | 15 | shred 16 | 17 | 18 | gnar_gnar 19 | 20 | gnar_gnar 21 | 22 | 23 | shred->gnar_gnar 24 | 25 | 26 | 27 | 28 | bluebird 29 | 30 | bluebird 31 | 32 | 33 | shred->bluebird 34 | 35 | 36 | 37 | 38 | weather_report 39 | 40 | weather_report 41 | 42 | 43 | shred->weather_report 44 | 45 | 46 | 47 | 48 | pow 49 | 50 | pow 51 | 52 | 53 | gnar_gnar->pow 54 | 55 | 56 | 57 | 58 | fluffy 59 | 60 | fluffy 61 | 62 | 63 | pow->fluffy 64 | 65 | 66 | 67 | 68 | soft 69 | 70 | soft 71 | 72 | 73 | pow->soft 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /guide/src/concepts/dominators-and-retained-size.md: -------------------------------------------------------------------------------- 1 | # Dominators and Retained Size 2 | 3 | Let's continue to use this example call graph: 4 | 5 | [Call Graph](./call-graph.svg) 6 | 7 | Imagine the `pow` function itself might is not very large. But it calls 8 | functions `soft` and `fluffy`, both of which are **huge**. And they are both 9 | *only* called by `pow`, so if `pow` were removed, then `soft` and `fluffy` would 10 | both become dead code and get removed as well. Therefore, `pow`'s "real" size is 11 | huge, even though it doesn't look like it at a glance. 12 | 13 | The *dominator* relationship gives us a way to reason about the *retained size* 14 | of a function. 15 | 16 | In a graph that is rooted at vertex *R*, vertex *A* is said to 17 | [*dominate*][dominators] vertex *B* if every path in the graph from *R* to *B* 18 | includes *A*. It follows that if *A* were removed from the graph, then *B* would 19 | become unreachable. 20 | 21 | In our call graphs, the roots are the `main` function (for executables) or 22 | publicly exported functions (for libraries). 23 | 24 | *V* is the *immediate dominator* of a vertex *U* if *V != U*, and there does not 25 | exist another distinct vertex *W* that is dominated by *V* but also dominates 26 | *U*. If we take all the vertices from a graph, remove the edges, and then add 27 | edges for each immediate dominator relationship, then we get a tree. Here is the 28 | dominator tree for our call graph from earlier, where `shred` is the root: 29 | 30 | [Dominator Tree](./dominator-tree.svg) 31 | 32 | Using the dominator relationship, 33 | [`twiggy` can](../usage/command-line-interface/dominators.md) help you find the 34 | *retained size* of some function by taking its shallow size and adding the 35 | retained sizes of each function that it immediately dominates. 36 | 37 | [dominators]: https://en.wikipedia.org/wiki/Dominator_(graph_theory) 38 | -------------------------------------------------------------------------------- /guide/src/concepts/generic-functions-and-monomorphization.md: -------------------------------------------------------------------------------- 1 | # Generic Functions and Monomorphization 2 | 3 | Generic functions with type parameters in Rust and template functions in C++ can 4 | lead to code bloat if you aren't careful. Every time you instantiate these 5 | generic functions with a concrete set of types, the compiler will *monomorphize* 6 | the function, creating a copy of its body replacing its generic placeholders 7 | with the specific operations that apply to the concrete types. This presents 8 | many opportunities for compiler optimizations based on which particular concrete 9 | types each copy of the function is working with, but these copies add up quickly 10 | in terms of code size. 11 | 12 | Example of monomorphization in Rust: 13 | 14 | ```rust 15 | fn generic_function(t: T) { ... } 16 | 17 | // Each of these will generate a new copy of `generic_function`! 18 | generic_function::(...); 19 | generic_function::(...); 20 | generic_function::(...); 21 | ``` 22 | 23 | Example of monomorphization in C++: 24 | 25 | ```c++ 26 | template 27 | void generic_function(T t) { ... } 28 | 29 | // Each of these will also generate a new copy of `generic_function`! 30 | generic_function(...); 31 | generic_function(...); 32 | generic_function(...); 33 | ``` 34 | 35 | If you can afford the runtime cost of dynamic dispatch, then changing these 36 | functions to use trait objects in Rust or virtual methods in C++ can likely save 37 | a significant amounts of code size. With dynamic dispatch, the generic 38 | function's body is not copied, and the generic bits within the function become 39 | indirect function calls. 40 | 41 | Example of dynamic dispatch in Rust: 42 | 43 | ```rust 44 | fn generic_function(t: &MyTrait) { ... } 45 | // or 46 | fn generic_function(t: Box) { ... } 47 | // etc... 48 | 49 | // No more code bloat! 50 | let x = MyTraitImpl::new(); 51 | generic_function(&x); 52 | let y = AnotherMyTraitImpl::new(); 53 | generic_function(&y); 54 | let z = MyTraitImplAlso::new(); 55 | generic_function(&z); 56 | ``` 57 | 58 | Example of dynamic dispatch in C++: 59 | 60 | ```c++ 61 | class GenericBase { 62 | public: 63 | virtual void generic_impl() = 0; 64 | }; 65 | 66 | class MyThing : public GenericBase { 67 | public 68 | virtual void generic_impl() override { ... } 69 | }; 70 | 71 | class AnotherThing : public GenericBase { 72 | public 73 | virtual void generic_impl() override { ... } 74 | }; 75 | 76 | class AlsoThing : public GenericBase { 77 | public 78 | virtual void generic_impl() override { ... } 79 | }; 80 | 81 | void generic(GenericBase& thing) { ... } 82 | 83 | // No more code bloat! 84 | MyThing x; 85 | generic(x); 86 | AnotherThing y; 87 | generic(y); 88 | AlsoThing z; 89 | generic(z); 90 | ``` 91 | 92 | [`twiggy` can analyze](../usage/command-line-interface/monos.md) a binary to 93 | find which generic functions are being monomorphized repeatedly, and calculate 94 | an estimation of how much code size could be saved by switching from 95 | monomorphization to dynamic dispatch. 96 | -------------------------------------------------------------------------------- /guide/src/concepts/index.md: -------------------------------------------------------------------------------- 1 | # 💡 Concepts 2 | 3 | This section provides some background knowledge on concepts that are useful to 4 | understand when using Twiggy. 5 | -------------------------------------------------------------------------------- /guide/src/concepts/paths.md: -------------------------------------------------------------------------------- 1 | # Paths 2 | 3 | If there is a *path* where *A → B → ... → C* through the [call 4 | graph](./call-graph.md), then we say that *C* is *reachable* through from 5 | *A*. *Dead code* is code that is not *reachable* in the call graph from any 6 | publicly exported functions (for libraries) or the `main` function (for 7 | executables). 8 | 9 | Recall our example call graph: 10 | 11 | [Call Graph](./call-graph.svg) 12 | 13 | Imagine that `shred` was our executable's `main` function. In this scenario, 14 | there is no path through the call graph from `shred` to `baker` or `hood`, so 15 | they are dead code. We would expect that the linker would remove them, and they 16 | wouldn't show up in the final binary. 17 | 18 | But what if some function that you *thought* was dead code is appearing inside 19 | your binary? Maybe it is deep down in some library you depend on, but inside a 20 | submodule of that library that you aren't using, and you wouldn't expect it to 21 | be included in the final binary. 22 | 23 | In this scenario, [`twiggy` can show](../usage/command-line-interface/paths.md) 24 | you all the paths in the call graph that lead to the unexpected function. This 25 | lets you understand why the unwelcome function is present, and decide what you 26 | can do about it. Maybe if you refactored your code to avoid calling *Y*, then 27 | there wouldn't be any paths to the unwelcome function anymore, it would be dead 28 | code, and the linker would remove it. 29 | -------------------------------------------------------------------------------- /guide/src/contributing.md: -------------------------------------------------------------------------------- 1 | # 🙌 Contributing to Twiggy 2 | -------------------------------------------------------------------------------- /guide/src/contributing/building.md: -------------------------------------------------------------------------------- 1 | # Building 2 | 3 | ## Building for the Native Target 4 | 5 | ``` 6 | $ cargo build --all --exclude twiggy-wasm-api 7 | ``` 8 | 9 | ## Building for the `wasm32-unknown-unknown` Target 10 | 11 | ``` 12 | $ JOB=wasm ./ci/script.sh 13 | ``` 14 | -------------------------------------------------------------------------------- /guide/src/contributing/code-formatting.md: -------------------------------------------------------------------------------- 1 | # Code Formatting 2 | 3 | We use [`rustfmt`](https://github.com/rust-lang-nursery/rustfmt) to enforce a 4 | consistent code style across the whole code base. 5 | 6 | You can install the latest version of `rustfmt` with this command: 7 | 8 | ``` 9 | $ rustup update 10 | $ rustup component add rustfmt --toolchain stable 11 | ``` 12 | 13 | Ensure that `~/.rustup/toolchains/$YOUR_HOST_TARGET/bin/` is on your `$PATH`. 14 | 15 | Once that is taken care of, you can (re)format all code by running this command 16 | from the root of the repository: 17 | 18 | ``` 19 | $ cargo fmt --all 20 | ``` 21 | 22 | # Linting 23 | 24 | We use [`clippy`](https://github.com/rust-lang/rust-clippy) to lint the codebase. 25 | This helps avoid common mistakes, and ensures that code is correct, 26 | performant, and idiomatic. 27 | 28 | You can install the latest version of `clippy` with this command: 29 | 30 | ``` 31 | $ rustup update 32 | $ rustup component add clippy --toolchain stable 33 | ``` 34 | 35 | Once that is complete, you can lint your code to check for mistakes by running 36 | this command from the root of the repository: 37 | 38 | ``` 39 | $ cargo clippy 40 | ``` 41 | -------------------------------------------------------------------------------- /guide/src/contributing/code-of-conduct.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | -------------------------------------------------------------------------------- /guide/src/contributing/index.md: -------------------------------------------------------------------------------- 1 | # 🙌 Contributing to Twiggy 2 | 3 | Hi! We'd love to have your contributions! If you want help or mentorship, reach 4 | out to us in a GitHub issue, or ping `fitzgen` in [`#rust-wasm` on 5 | `irc.mozilla.org`](irc://irc.mozilla.org#rust-wasm) and introduce yourself. 6 | 7 | ## Code of Conduct 8 | 9 | We abide by the [Rust Code of Conduct][coc] and ask that you do as well. 10 | 11 | [coc]: https://www.rust-lang.org/en-US/conduct.html 12 | -------------------------------------------------------------------------------- /guide/src/contributing/pull-requests.md: -------------------------------------------------------------------------------- 1 | # Pull Requests 2 | 3 | All pull requests must be reviewed and approved of by at least one 4 | [team](./team.html) member before merging. See [Contributions We 5 | Want](#contributions-we-want) for details on what should be included in what 6 | kind of pull request. 7 | 8 | ## Contributions We Want 9 | 10 | * **Bug fixes!** Include a regression test. 11 | 12 | * **Support for more binary formats!** See [this issue][more-formats] for 13 | details. 14 | 15 | * **New analyses and queries!** Help expose information about monomorphizations 16 | or inlining. Report diffs between two versions of the same binary. Etc... 17 | 18 | If you make two of these kinds of contributions, you should seriously consider 19 | joining our [team](./team.html)! 20 | 21 | ## Where We Need Help 22 | 23 | * Issues labeled ["help wanted"][help-wanted] are issues where we could use a 24 | little help from you. 25 | 26 | * Issues labeled ["mentored"][mentored] are issues that don't really involve any 27 | more investigation, just implementation. We've outlined what needs to be done, 28 | and a [team](./team.html) member has volunteered to help whoever claims the 29 | issue to implement it, and get the implementation merged. 30 | 31 | * Issues labeled ["good first issue"][gfi] are issues where fixing them would be 32 | a great introduction to the code base. 33 | 34 | [more-formats]: https://github.com/rustwasm/twiggy/issues/4 35 | [help-wanted]: https://github.com/rustwasm/twiggy/labels/help%20wanted 36 | [mentored]: https://github.com/rustwasm/twiggy/labels/mentored 37 | [gfi]: https://github.com/rustwasm/twiggy/labels/good%20first%20issue 38 | -------------------------------------------------------------------------------- /guide/src/contributing/team.md: -------------------------------------------------------------------------------- 1 | # Team 2 | 3 | ## Members 4 | 5 | | [fitzgen](https://github.com/fitzgen) | [data-pup](https://github.com/data-pup) | 6 | |:---:|:---:| 7 | | [`fitzgen`](https://github.com/fitzgen) | [`data-pup`](https://github.com/data-pup) | 8 | 9 | ## Responsibilities 10 | 11 | Team members review pull requests, triage new issues, mentor new contributors, 12 | and maintain the Twiggy project. 13 | 14 | Larger, more nuanced decisions about design, architecture, breaking changes, 15 | trade offs, etc are made by team consensus. In other words, decisions on things 16 | that aren't straightforward improvements or bug fixes to things that already 17 | exist in `twiggy`. If consensus can't be made, then `fitzgen` has the last 18 | word. 19 | 20 | **We need more team members!** 21 | [Drop a comment on this issue if you are interested in joining.][join] 22 | 23 | [join]: https://github.com/rustwasm/twiggy/issues/3 24 | -------------------------------------------------------------------------------- /guide/src/contributing/testing.md: -------------------------------------------------------------------------------- 1 | # Testing 2 | 3 | ``` 4 | $ cargo test --all --exclude twiggy-wasm-api 5 | ``` 6 | 7 | ## Authoring New Tests 8 | 9 | Integration tests live in the `twiggy/tests` directory: 10 | 11 | ``` 12 | twiggy/tests 13 | ├── expectations 14 | ├── fixtures 15 | └── tests.rs 16 | ``` 17 | 18 | * The `twiggy/tests/tests.rs` file contains the `#[test]` definitions. 19 | 20 | * The `twiggy/tests/fixtures` directory contains input binaries for tests. 21 | 22 | * The `twiggy/tests/expectations` directory contains the expected output of test 23 | commands. 24 | 25 | ## Updating Test Expectations 26 | 27 | To automatically update all test expectations, you can run the tests with the 28 | `TWIGGY_UPDATE_TEST_EXPECTATIONS=1` environment variable set. Make sure that you 29 | look at the changes before committing them, and that they match your intentions! 30 | 31 | TIP: You can use `git add -p` to examine individual hunks when staging changes 32 | before committing! 33 | -------------------------------------------------------------------------------- /guide/src/index.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |

Twiggy🌱

4 | 5 | A code size profiler for Wasm 6 | 7 |

8 | 9 | 10 | 11 | 12 |

13 | 14 |

15 | Guide 16 | | 17 | Contributing 18 | | 19 | Chat 20 |

21 | 22 | Built with 🦀🕸 by The Rust and WebAssembly Working Group 23 |
24 | 25 | ## About 26 | 27 | Twiggy is a code size profiler for Wasm. It analyzes a binary's call graph to 28 | answer questions like: 29 | 30 | * Why was this function included in the binary in the first place? Who calls it? 31 | 32 | * What is the *retained size* of this function? I.e. how much space would be 33 | saved if I removed it and all the functions that become dead code after its 34 | removal. 35 | 36 | Use Twiggy to make your binaries slim! 37 | 38 |
39 | 40 |
41 | -------------------------------------------------------------------------------- /guide/src/install.md: -------------------------------------------------------------------------------- 1 | # 📦 Install 2 | 3 | First, ensure that you have the [Rust toolchain installed](https://www.rust-lang.org/). 4 | Then, run: 5 | ``` 6 | cargo install twiggy 7 | ``` 8 | -------------------------------------------------------------------------------- /guide/src/supported-binary-formats.md: -------------------------------------------------------------------------------- 1 | # 🔎 Supported Binary Formats 2 | 3 | ## Full Support 4 | 5 | `twiggy` currently supports these binary formats: 6 | 7 | * ✔️ WebAssembly's `.wasm` format 8 | 9 | ## Partial, Work-in-Progress Support 10 | 11 | `twiggy` has partial, work-in-progress support for these binary formats *when 12 | they have [DWARF][dwarf] debug info*: 13 | 14 | * ⚠ ELF 15 | * ⚠ Mach-O 16 | 17 | ## Unsupported 18 | 19 | * ❌ PE/COFF 20 | 21 | Although `twiggy` doesn't currently support these binary formats, it is designed 22 | with extensibility in mind. The input is translated into a format-agnostic 23 | internal representation (IR), and adding support for new formats only requires 24 | parsing them into this IR. The vast majority of `twiggy` will not need 25 | modification. 26 | 27 | We would love to gain support for new binary formats! If you're interested in 28 | helping out with that implementation work, [read this to learn how to contribute 29 | to Twiggy!](./contributing/index.html) 30 | 31 | [dwarf]: http://dwarfstd.org/ 32 | -------------------------------------------------------------------------------- /guide/src/theme/css/general.css: -------------------------------------------------------------------------------- 1 | /* Base styles and content styles */ 2 | 3 | @import 'variables.css'; 4 | 5 | html { 6 | font-family: "Open Sans", sans-serif; 7 | color: var(--fg); 8 | background-color: var(--bg); 9 | text-size-adjust: none; 10 | } 11 | 12 | body { 13 | margin: 0; 14 | font-size: 1rem; 15 | overflow-x: hidden; 16 | } 17 | 18 | /* code { */ 19 | /* font-family: "Source Code Pro", Consolas, "Ubuntu Mono", Menlo, "DejaVu Sans Mono", monospace, monospace; */ 20 | /* font-size: 0.875em; /\* please adjust the ace font size accordingly in editor.js *\/ */ 21 | /* } */ 22 | 23 | .left { float: left; } 24 | .right { float: right; } 25 | .hidden { display: none; } 26 | .play-button.hidden { display: none; } 27 | 28 | h2, h3 { margin-top: 2.5em; } 29 | h4, h5 { margin-top: 2em; } 30 | 31 | .header + .header h3, 32 | .header + .header h4, 33 | .header + .header h5 { 34 | margin-top: 1em; 35 | } 36 | 37 | a.header:target h1:before, 38 | a.header:target h2:before, 39 | a.header:target h3:before, 40 | a.header:target h4:before { 41 | display: inline-block; 42 | content: "»"; 43 | margin-left: -30px; 44 | width: 30px; 45 | } 46 | 47 | .page { 48 | outline: 0; 49 | padding: 0 var(--page-padding); 50 | } 51 | .page-wrapper { 52 | box-sizing: border-box; 53 | } 54 | .js .page-wrapper { 55 | transition: margin-left 0.3s ease, transform 0.3s ease; /* Animation: slide away */ 56 | } 57 | 58 | .content { 59 | overflow-y: auto; 60 | padding: 0 15px; 61 | padding-bottom: 50px; 62 | } 63 | .content main { 64 | margin-left: auto; 65 | margin-right: auto; 66 | max-width: var(--content-max-width); 67 | } 68 | .content a { text-decoration: none; } 69 | .content a:hover { text-decoration: underline; } 70 | .content img { max-width: 100%; } 71 | .content .header:link, 72 | .content .header:visited { 73 | color: var(--fg); 74 | } 75 | .content .header:link, 76 | .content .header:visited:hover { 77 | text-decoration: none; 78 | } 79 | 80 | table { 81 | margin: 0 auto; 82 | border-collapse: collapse; 83 | } 84 | table td { 85 | padding: 3px 20px; 86 | border: 1px var(--table-border-color) solid; 87 | } 88 | table thead { 89 | background: var(--table-header-bg); 90 | } 91 | table thead td { 92 | font-weight: 700; 93 | border: none; 94 | } 95 | table thead tr { 96 | border: 1px var(--table-header-bg) solid; 97 | } 98 | /* Alternate background colors for rows */ 99 | table tbody tr:nth-child(2n) { 100 | background: var(--table-alternate-bg); 101 | } 102 | 103 | 104 | blockquote { 105 | margin: 20px 0; 106 | padding: 0 20px; 107 | color: var(--fg); 108 | background-color: var(--quote-bg); 109 | border-top: .1em solid var(--quote-border); 110 | border-bottom: .1em solid var(--quote-border); 111 | } 112 | 113 | 114 | :not(.footnote-definition) + .footnote-definition, 115 | .footnote-definition + :not(.footnote-definition) { 116 | margin-top: 2em; 117 | } 118 | .footnote-definition { 119 | font-size: 0.9em; 120 | margin: 0.5em 0; 121 | } 122 | .footnote-definition p { 123 | display: inline; 124 | } 125 | 126 | .tooltiptext { 127 | position: absolute; 128 | visibility: hidden; 129 | color: #fff; 130 | background-color: #333; 131 | transform: translateX(-50%); /* Center by moving tooltip 50% of its width left */ 132 | left: -8px; /* Half of the width of the icon */ 133 | top: -35px; 134 | font-size: 0.8em; 135 | text-align: center; 136 | border-radius: 6px; 137 | padding: 5px 8px; 138 | margin: 5px; 139 | z-index: 1000; 140 | } 141 | .tooltipped .tooltiptext { 142 | visibility: visible; 143 | } 144 | -------------------------------------------------------------------------------- /guide/src/theme/css/print.css: -------------------------------------------------------------------------------- 1 | 2 | #sidebar, 3 | #menu-bar, 4 | .nav-chapters, 5 | .mobile-nav-chapters { 6 | display: none; 7 | } 8 | 9 | #page-wrapper.page-wrapper { 10 | transform: none; 11 | margin-left: 0px; 12 | overflow-y: initial; 13 | } 14 | 15 | #content { 16 | max-width: none; 17 | margin: 0; 18 | padding: 0; 19 | } 20 | 21 | .page { 22 | overflow-y: initial; 23 | } 24 | 25 | code { 26 | background-color: #666666; 27 | border-radius: 5px; 28 | 29 | /* Force background to be printed in Chrome */ 30 | -webkit-print-color-adjust: exact; 31 | } 32 | 33 | pre > .buttons { 34 | z-index: 2; 35 | } 36 | 37 | a, a:visited, a:active, a:hover { 38 | color: #4183c4; 39 | text-decoration: none; 40 | } 41 | 42 | h1, h2, h3, h4, h5, h6 { 43 | page-break-inside: avoid; 44 | page-break-after: avoid; 45 | } 46 | 47 | pre, code { 48 | page-break-inside: avoid; 49 | white-space: pre-wrap; 50 | } 51 | 52 | .fa { 53 | display: none !important; 54 | } 55 | -------------------------------------------------------------------------------- /guide/src/theme/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustwasm/twiggy/f6230a8a0f0d262695a66016bbd92a4f1aef46ee/guide/src/theme/favicon.png -------------------------------------------------------------------------------- /guide/src/theme/highlight.css: -------------------------------------------------------------------------------- 1 | /* Base16 Atelier Dune Light - Theme */ 2 | /* by Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/dune) */ 3 | /* Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) */ 4 | 5 | /* Atelier-Dune Comment */ 6 | .hljs-comment, 7 | .hljs-quote { 8 | color: #AAA; 9 | } 10 | 11 | /* Atelier-Dune Red */ 12 | .hljs-variable, 13 | .hljs-template-variable, 14 | .hljs-attribute, 15 | .hljs-tag, 16 | .hljs-name, 17 | .hljs-regexp, 18 | .hljs-link, 19 | .hljs-name, 20 | .hljs-selector-id, 21 | .hljs-selector-class { 22 | color: #d73737; 23 | } 24 | 25 | /* Atelier-Dune Orange */ 26 | .hljs-number, 27 | .hljs-meta, 28 | .hljs-built_in, 29 | .hljs-builtin-name, 30 | .hljs-literal, 31 | .hljs-type, 32 | .hljs-params { 33 | color: #b65611; 34 | } 35 | 36 | /* Atelier-Dune Green */ 37 | .hljs-string, 38 | .hljs-symbol, 39 | .hljs-bullet { 40 | color: #60ac39; 41 | } 42 | 43 | /* Atelier-Dune Blue */ 44 | .hljs-title, 45 | .hljs-section { 46 | color: #6684e1; 47 | } 48 | 49 | /* Atelier-Dune Purple */ 50 | .hljs-keyword, 51 | .hljs-selector-tag { 52 | color: #b854d4; 53 | } 54 | 55 | .hljs { 56 | display: block; 57 | overflow-x: auto; 58 | background: #f1f1f1; 59 | color: #6e6b5e; 60 | padding: 0.5em; 61 | } 62 | 63 | .hljs-emphasis { 64 | font-style: italic; 65 | } 66 | 67 | .hljs-strong { 68 | font-weight: bold; 69 | } 70 | -------------------------------------------------------------------------------- /guide/src/twiggy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustwasm/twiggy/f6230a8a0f0d262695a66016bbd92a4f1aef46ee/guide/src/twiggy.png -------------------------------------------------------------------------------- /guide/src/usage/as-a-crate.md: -------------------------------------------------------------------------------- 1 | # 🦀 As a Crate 2 | 3 | `twiggy` is divided into a collection of crates that you can use 4 | programmatically, but no long-term stability is promised. We will follow semver 5 | as best as we can, but will err on the side of being more conservative with 6 | breaking version bumps than might be strictly necessary. 7 | 8 | Here is a simple example: 9 | 10 | ```rust 11 | extern crate twiggy_analyze; 12 | extern crate twiggy_opt; 13 | extern crate twiggy_parser; 14 | 15 | use std::fs; 16 | use std::io; 17 | 18 | fn main() { 19 | let mut file = fs::File::open("path/to/some/binary").unwrap(); 20 | let mut data = vec![]; 21 | file.read_to_end(&mut data).unwrap(); 22 | 23 | let items = twiggy_parser::parse(&data).unwrap(); 24 | 25 | let options = twiggy_opt::Top::default(); 26 | let top = twiggy_analyze::top(&mut items, &options).unwrap(); 27 | 28 | let mut stdout = io::stdout(); 29 | top.emit_text(&items, &mut stdout).unwrap(); 30 | } 31 | ``` 32 | 33 | For a more in-depth example, take a look at the implementation of the 34 | `twiggy` CLI crate. 35 | -------------------------------------------------------------------------------- /guide/src/usage/command-line-interface/diff.md: -------------------------------------------------------------------------------- 1 | # `twiggy diff` 2 | 3 | The `twiggy diff` sub-command computes the delta size of each item between old 4 | and new versions of a binary. 5 | 6 | ``` 7 | Delta Bytes │ Item 8 | ─────────────┼────────────────────────────────────────────── 9 | -1034 ┊ data[3] 10 | -593 ┊ "function names" subsection 11 | +396 ┊ wee_alloc::alloc_first_fit::he2a4ddf96981c0ce 12 | +243 ┊ goodbye 13 | -226 ┊ wee_alloc::alloc_first_fit::h9a72de3af77ef93f 14 | -262 ┊ ... and 29 more. 15 | -1476 ┊ Σ [34 Total Rows] 16 | ``` 17 | -------------------------------------------------------------------------------- /guide/src/usage/command-line-interface/dominators.md: -------------------------------------------------------------------------------- 1 | # `twiggy dominators` 2 | 3 | The `twiggy dominators` sub-command displays the dominator tree of a binary's 4 | call graph. 5 | 6 | ``` 7 | Retained Bytes │ Retained % │ Dominator Tree 8 | ────────────────┼────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 9 | 175726 ┊ 14.99% ┊ export "items_parse" 10 | 175712 ┊ 14.98% ┊ ⤷ items_parse 11 | 131407 ┊ 11.21% ┊ ⤷ twiggy_parser::wasm_parse::::parse_items::h39c45381d868d181 12 | 18492 ┊ 1.58% ┊ ⤷ wasmparser::binary_reader::BinaryReader::read_operator::hb1c7cde18e148939 13 | 2677 ┊ 0.23% ┊ ⤷ alloc::collections::btree::map::BTreeMap::insert::hd2463626e5ac3441 14 | 1349 ┊ 0.12% ┊ ⤷ wasmparser::readers::module::ModuleReader::read::hb76af8efd547784f 15 | 1081 ┊ 0.09% ┊ ⤷ core::ops::function::impls:: for &mut F>::call_once::h1ff7fe5b944492c3 16 | 776 ┊ 0.07% ┊ ⤷ ::read::h12903e6d8d4091bd 17 | ``` 18 | -------------------------------------------------------------------------------- /guide/src/usage/command-line-interface/garbage.md: -------------------------------------------------------------------------------- 1 | # `twiggy garbage` 2 | 3 | The `twiggy garbage` sub-command finds and displays dead code and data that is 4 | not transitively referenced by any exports or public functions. 5 | 6 | ``` 7 | Bytes │ Size % │ Garbage Item 8 | ───────┼────────┼──────────────────────────────── 9 | 12 ┊ 6.09% ┊ unusedAddThreeNumbers 10 | 9 ┊ 4.57% ┊ unusedAddOne 11 | 7 ┊ 3.55% ┊ type[2]: (i32, i32, i32) -> i32 12 | 6 ┊ 3.05% ┊ unusedChild 13 | 5 ┊ 2.54% ┊ type[1]: (i32) -> i32 14 | 4 ┊ 2.03% ┊ type[0]: () -> i32 15 | 43 ┊ 21.83% ┊ Σ [6 Total Rows] 16 | ``` 17 | -------------------------------------------------------------------------------- /guide/src/usage/command-line-interface/index.md: -------------------------------------------------------------------------------- 1 | # ⌨ Command Line Interface 2 | 3 | `twiggy` is primarily a command line tool. 4 | 5 | To get the most up-to-date usage for the version of `twiggy` that you have 6 | installed, you can always run: 7 | 8 | ``` 9 | twiggy --help 10 | ``` 11 | 12 | Or, to get more information about a sub-command, run: 13 | 14 | ``` 15 | twiggy subcmd --help 16 | ``` 17 | -------------------------------------------------------------------------------- /guide/src/usage/command-line-interface/monos.md: -------------------------------------------------------------------------------- 1 | # `twiggy monos` 2 | 3 | The `twiggy monos` sub-command lists the generic function monomorphizations that 4 | are contributing to code bloat. 5 | 6 | ``` 7 | Apprx. Bloat Bytes │ Apprx. Bloat % │ Bytes │ % │ Monomorphizations 8 | ────────────────────┼────────────────┼───────┼────────┼──────────────────────────────────────────────────────── 9 | 2141 ┊ 3.68% ┊ 3249 ┊ 5.58% ┊ alloc::slice::merge_sort 10 | ┊ ┊ 1108 ┊ 1.90% ┊ alloc::slice::merge_sort::hb3d195f9800bdad6 11 | ┊ ┊ 1108 ┊ 1.90% ┊ alloc::slice::merge_sort::hfcf2318d7dc71d03 12 | ┊ ┊ 1033 ┊ 1.77% ┊ alloc::slice::merge_sort::hcfca67f5c75a52ef 13 | 1457 ┊ 2.50% ┊ 4223 ┊ 7.26% ┊ <&'a T as core::fmt::Debug>::fmt 14 | ┊ ┊ 2766 ┊ 4.75% ┊ <&'a T as core::fmt::Debug>::fmt::h1c27955d8de3ff17 15 | ┊ ┊ 636 ┊ 1.09% ┊ <&'a T as core::fmt::Debug>::fmt::hea6a77c4dcddb7ac 16 | ┊ ┊ 481 ┊ 0.83% ┊ <&'a T as core::fmt::Debug>::fmt::hfbacf6f5c9f53bb2 17 | ┊ ┊ 340 ┊ 0.58% ┊ ... and 1 more. 18 | 3759 ┊ 6.46% ┊ 31160 ┊ 53.54% ┊ ... and 214 more. 19 | 7357 ┊ 12.64% ┊ 38632 ┊ 66.37% ┊ Σ [223 Total Rows] 20 | ``` 21 | -------------------------------------------------------------------------------- /guide/src/usage/command-line-interface/paths.md: -------------------------------------------------------------------------------- 1 | # `twiggy paths` 2 | 3 | The `twiggy paths` sub-command finds the call paths to a function in the given 4 | binary's call graph. This tells you what other functions are calling this 5 | function, why this function is not dead code, and therefore why it wasn't 6 | removed by the linker. 7 | 8 | ``` 9 | Shallow Bytes │ Shallow % │ Retaining Paths 10 | ───────────────┼───────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 11 | 153 ┊ 5.43% ┊ wee_alloc::alloc_with_refill::hb32c1bbce9ebda8e 12 | ┊ ┊ ⬑ as wee_alloc::AllocPolicy>::new_cell_for_free_list::h3987e3054b8224e6 13 | ┊ ┊ ⬑ elem[0] 14 | ┊ ┊ ⬑ table[0] 15 | ┊ ┊ ⬑ hello 16 | ┊ ┊ ⬑ export "hello" 17 | 18 | ``` 19 | -------------------------------------------------------------------------------- /guide/src/usage/command-line-interface/top.md: -------------------------------------------------------------------------------- 1 | # `twiggy top` 2 | 3 | The `twiggy top` sub-command summarizes and lists the top code size offenders in 4 | a binary. 5 | 6 | ``` 7 | Shallow Bytes │ Shallow % │ Item 8 | ───────────────┼───────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 9 | 1034 ┊ 36.71% ┊ data[3] 10 | 777 ┊ 27.58% ┊ "function names" subsection 11 | 226 ┊ 8.02% ┊ wee_alloc::alloc_first_fit::h9a72de3af77ef93f 12 | 165 ┊ 5.86% ┊ hello 13 | 153 ┊ 5.43% ┊ wee_alloc::alloc_with_refill::hb32c1bbce9ebda8e 14 | 137 ┊ 4.86% ┊ as wee_alloc::AllocPolicy>::new_cell_for_free_list::h3987e3054b8224e6 15 | 77 ┊ 2.73% ┊ ::new_cell_for_free_list::h8f071b7bce0301ba 16 | 45 ┊ 1.60% ┊ goodbye 17 | 25 ┊ 0.89% ┊ data[1] 18 | 25 ┊ 0.89% ┊ data[2] 19 | 153 ┊ 5.43% ┊ ... and 27 more. 20 | 2817 ┊ 100.00% ┊ Σ [37 Total Rows] 21 | ``` 22 | -------------------------------------------------------------------------------- /guide/src/usage/index.md: -------------------------------------------------------------------------------- 1 | # 🏋️‍♀️ Usage 2 | 3 | Twiggy is primarily a command line tool, but it can also be used as a library 4 | crate from within other Rust projects, or compiled to WebAssembly and used from 5 | JavaScript on the Web or from Node.js 6 | 7 | 8 | ## 🏋️‍♀️ wasm-pack 9 | 10 | In order to get usable output with `wasm-pack`, we need debug info in the resulting `.wasm` file. 11 | 12 | Add this to your Cargo.toml 13 | ``` 14 | [package.metadata.wasm-pack.profile.release] 15 | wasm-opt = ['-g', '-O'] 16 | ``` 17 | -------------------------------------------------------------------------------- /guide/src/usage/on-the-web-with-webassembly.md: -------------------------------------------------------------------------------- 1 | # 🕸 On the Web with WebAssembly 2 | 3 | First, ensure you have the `wasm32-unknown-unknown` Rust target installed and 4 | up-to-date: 5 | 6 | ``` 7 | rustup target add wasm32-unknown-unknown 8 | ``` 9 | 10 | Next, install `wasm-bindgen`: 11 | 12 | ``` 13 | cargo install wasm-bindgen-cli 14 | ``` 15 | 16 | Finally, build `twiggy`'s WebAssembly API with `wasm-bindgen`: 17 | 18 | ``` 19 | cd twiggy/wasm-api 20 | cargo build --release --target wasm32-unknown-unknown 21 | wasm-bindgen ../target/wasm32-unknown-unknown/release/twiggy_wasm_api.wasm --out-dir . 22 | ``` 23 | 24 | This should produce two artifacts in the current directory: 25 | 26 | 1. `twiggy_wasm_api_bg.wasm`: The WebAssembly file containing `twiggy`. 27 | 2. `twiggy_wasm_api.js`: The JavaScript bindings to `twiggy`'s WebAssembly. 28 | 29 | You can now use `twiggy` from JavaScript like this: 30 | 31 | ```js 32 | import { Items, Monos } from './twiggy_wasm_api'; 33 | 34 | // Parse a binary's data into a collection of items. 35 | const items = Items.parse(myData); 36 | 37 | // Configure an analysis and its options. 38 | const opts = Monos.new(); 39 | opts.set_max_generics(10); 40 | opts.set_max_monos(10); 41 | 42 | // Run the analysis on the parsed items. 43 | const monos = JSON.parse(items.monos(opts)); 44 | ``` 45 | -------------------------------------------------------------------------------- /ir/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Nick Fitzgerald "] 3 | categories = [] 4 | description = "Intermediate representation for the Twiggy code size profiler." 5 | license = "Apache-2.0/MIT" 6 | name = "twiggy-ir" 7 | readme = "../README.md" 8 | repository = "https://github.com/rustwasm/twiggy" 9 | version = "0.7.0" 10 | edition = "2018" 11 | 12 | [lib] 13 | path = "./ir.rs" 14 | 15 | [dependencies] 16 | cpp_demangle = { version = "0.3.5", default-features = false } 17 | frozen = "1" 18 | petgraph = "0.6.2" 19 | rustc-demangle = "0.1.21" 20 | serde = "1.0" 21 | serde_derive = "1.0" 22 | -------------------------------------------------------------------------------- /ir/graph_impl.rs: -------------------------------------------------------------------------------- 1 | use super::{Id, Items, Neighbors}; 2 | use petgraph::visit; 3 | use std::collections::HashSet; 4 | 5 | impl visit::GraphBase for Items { 6 | type EdgeId = (); 7 | type NodeId = Id; 8 | } 9 | 10 | impl visit::Visitable for Items { 11 | type Map = HashSet; 12 | 13 | #[inline] 14 | fn visit_map(&self) -> Self::Map { 15 | HashSet::with_capacity(self.items.len()) 16 | } 17 | 18 | #[inline] 19 | fn reset_map(&self, map: &mut Self::Map) { 20 | map.clear(); 21 | } 22 | } 23 | 24 | impl<'a> visit::IntoNeighbors for &'a Items { 25 | type Neighbors = Neighbors<'a>; 26 | 27 | #[inline] 28 | fn neighbors(self, id: Id) -> Self::Neighbors { 29 | self.neighbors(id) 30 | } 31 | } 32 | 33 | impl visit::NodeCount for Items { 34 | #[inline] 35 | fn node_count(&self) -> usize { 36 | self.items.len() 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /job_runner/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "job_runner" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | [dependencies] 7 | rustop = "1.1" 8 | cargo_toml = "0.13" 9 | anyhow = "1.0" -------------------------------------------------------------------------------- /opt/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Nick Fitzgerald "] 3 | categories = [] 4 | description = "Options for the Twiggy code size profiler." 5 | license = "Apache-2.0/MIT" 6 | name = "twiggy-opt" 7 | readme = "../README.md" 8 | repository = "https://github.com/rustwasm/twiggy" 9 | version = "0.7.0" 10 | edition = "2018" 11 | 12 | [lib] 13 | path = "opt.rs" 14 | 15 | [build-dependencies] 16 | regex = "1.4.2" 17 | 18 | [dependencies] 19 | anyhow = "1.0" 20 | structopt = { version = "0.3", optional = true } 21 | twiggy-traits = { version = "=0.7.0", path = "../traits" } 22 | wasm-bindgen = { version = "0.2.100", optional = true } 23 | cfg-if = "1.0.0" 24 | 25 | [features] 26 | default = ["cli", "emit_csv", "emit_json", "emit_text"] 27 | cli = ["structopt"] 28 | wasm = ["wasm-bindgen"] 29 | emit_json = ["twiggy-traits/emit_json"] 30 | emit_text = ["twiggy-traits/emit_text"] 31 | emit_csv = ["twiggy-traits/emit_csv"] 32 | -------------------------------------------------------------------------------- /opt/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::fs; 3 | use std::io::{self, BufRead, Write}; 4 | use std::path::{Path, PathBuf}; 5 | 6 | fn main() { 7 | let mut cli = PathBuf::new(); 8 | cli.push(env::var("OUT_DIR").expect("should have OUT_DIR env var")); 9 | 10 | let mut wasm = cli.clone(); 11 | wasm.push("wasm.rs"); 12 | 13 | cli.push("cli.rs"); 14 | 15 | println!("cargo:rerun-if-changed=./definitions.rs"); 16 | println!("cargo:rerun-if-changed=./build.rs"); 17 | 18 | copy_without_lines_matching_pattern("definitions.rs", cli, ".*\\bwasm_bindgen\\b.*"); 19 | copy_without_lines_matching_pattern("definitions.rs", wasm, ".*\\bstructopt\\b.*"); 20 | } 21 | 22 | fn copy_without_lines_matching_pattern(from: P1, to: P2, pattern: S) 23 | where 24 | P1: AsRef, 25 | P2: AsRef, 26 | S: AsRef, 27 | { 28 | let from = from.as_ref(); 29 | let from = fs::File::open(from).expect(&format!("should open `{}` OK", from.display())); 30 | let from = io::BufReader::new(from); 31 | 32 | let to = to.as_ref(); 33 | let to = fs::File::create(to).expect(&format!("should open `{}` OK", to.display())); 34 | let mut to = io::BufWriter::new(to); 35 | 36 | let pattern_str = pattern.as_ref(); 37 | let pattern = regex::RegexBuilder::new(pattern_str) 38 | .case_insensitive(true) 39 | .build() 40 | .expect(&format!("should create regex from '{}' OK", pattern_str)); 41 | 42 | for line in from.lines() { 43 | let line = line.expect("should read line OK"); 44 | 45 | if pattern.is_match(&line) { 46 | continue; 47 | } 48 | 49 | to.write_all(line.as_bytes()).expect("should write line OK"); 50 | to.write_all(b"\n").expect("should write newline OK"); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /parser/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Nick Fitzgerald "] 3 | categories = [] 4 | description = "Common traits for the Twiggy code size profiler." 5 | license = "Apache-2.0/MIT" 6 | name = "twiggy-parser" 7 | readme = "../README.md" 8 | repository = "https://github.com/rustwasm/twiggy" 9 | version = "0.7.0" 10 | edition = "2018" 11 | 12 | [lib] 13 | path = "./parser.rs" 14 | 15 | [dependencies] 16 | anyhow = "1.0" 17 | object = { version = "0.36.7", optional = true } 18 | wasmparser = "0.230.0" 19 | twiggy-ir = { version = "=0.7.0", path = "../ir" } 20 | twiggy-traits = { version = "=0.7.0", path = "../traits" } 21 | 22 | [features] 23 | default = ["dwarf"] 24 | dwarf = ["object", "twiggy-traits/dwarf"] 25 | -------------------------------------------------------------------------------- /parser/parser.rs: -------------------------------------------------------------------------------- 1 | //! Parses binaries into `twiggy_ir::Items`. 2 | 3 | #![deny(missing_docs)] 4 | #![deny(missing_debug_implementations)] 5 | 6 | use std::ffi::OsStr; 7 | use std::fs; 8 | use std::io::Read; 9 | use std::path; 10 | 11 | use twiggy_ir as ir; 12 | use twiggy_traits as traits; 13 | 14 | #[cfg(feature = "dwarf")] 15 | mod object_parse; 16 | mod wasm_parse; 17 | 18 | const WASM_MAGIC_NUMBER: [u8; 4] = [0x00, 0x61, 0x73, 0x6D]; 19 | 20 | /// Parse the file at the given path into IR items. 21 | pub fn read_and_parse>( 22 | path: P, 23 | mode: traits::ParseMode, 24 | ) -> anyhow::Result { 25 | let path = path.as_ref(); 26 | let mut file = fs::File::open(path)?; 27 | let mut data = vec![]; 28 | file.read_to_end(&mut data)?; 29 | 30 | match mode { 31 | traits::ParseMode::Wasm => parse_wasm(&data), 32 | #[cfg(feature = "dwarf")] 33 | traits::ParseMode::Dwarf => parse_other(&data), 34 | traits::ParseMode::Auto => parse_auto(path.extension(), &data), 35 | } 36 | } 37 | 38 | /// Parse the given data into IR items. 39 | pub fn parse(data: &[u8]) -> anyhow::Result { 40 | parse_fallback(data) 41 | } 42 | 43 | /// A trait for parsing things into `ir::Item`s. 44 | pub(crate) trait Parse<'a> { 45 | /// Any extra data needed to parse this type's items. 46 | type ItemsExtra; 47 | 48 | /// Parse `Self` into one or more `ir::Item`s and add them to the builder. 49 | fn parse_items( 50 | self, 51 | items: &mut ir::ItemsBuilder, 52 | extra: Self::ItemsExtra, 53 | ) -> anyhow::Result<()>; 54 | 55 | /// Any extra data needed to parse this type's edges. 56 | type EdgesExtra; 57 | 58 | /// Parse edges between items. This is only called *after* we have already 59 | /// parsed items. 60 | fn parse_edges( 61 | self, 62 | items: &mut ir::ItemsBuilder, 63 | extra: Self::EdgesExtra, 64 | ) -> anyhow::Result<()>; 65 | } 66 | 67 | fn parse_auto(extension: Option<&OsStr>, data: &[u8]) -> anyhow::Result { 68 | if sniff_wasm(extension, &data) { 69 | parse_wasm(&data) 70 | } else { 71 | #[cfg(feature = "dwarf")] 72 | let res = parse_other(&data); 73 | #[cfg(not(feature = "dwarf"))] 74 | let res = parse_fallback(&data); 75 | res 76 | } 77 | } 78 | 79 | fn sniff_wasm(extension: Option<&OsStr>, data: &[u8]) -> bool { 80 | match extension.and_then(|s| s.to_str()) { 81 | Some("wasm") => true, 82 | _ => data.get(0..4) == Some(&WASM_MAGIC_NUMBER), 83 | } 84 | } 85 | 86 | fn parse_wasm(data: &[u8]) -> anyhow::Result { 87 | let mut items = ir::ItemsBuilder::new(data.len() as u32); 88 | 89 | let module1 = wasm_parse::ModuleReader::new(data); 90 | module1.parse_items(&mut items, ())?; 91 | let module2 = wasm_parse::ModuleReader::new(data); 92 | module2.parse_edges(&mut items, ())?; 93 | 94 | Ok(items.finish()) 95 | } 96 | 97 | #[cfg(feature = "dwarf")] 98 | fn parse_other(data: &[u8]) -> anyhow::Result { 99 | object_parse::parse(&data) 100 | } 101 | 102 | fn parse_fallback(data: &[u8]) -> anyhow::Result { 103 | parse_wasm(data) 104 | } 105 | -------------------------------------------------------------------------------- /publish.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eux 4 | 5 | cd "$(dirname $0)" 6 | 7 | for dir in ir traits parser opt analyze twiggy; do 8 | cd "$dir" 9 | 10 | if [[ "$dir" == "opt" || "$dir" == "analyze" ]]; then 11 | cargo publish --no-verify 12 | else 13 | cargo publish 14 | fi 15 | 16 | cd - 17 | done 18 | -------------------------------------------------------------------------------- /releases/friends.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eu 4 | set -o pipefail 5 | 6 | cd $(dirname $0) 7 | 8 | if [[ -z "${1+x}" ]]; then 9 | read -p "List friends since which commit/tag? " since 10 | echo 11 | else 12 | since=$1 13 | fi 14 | 15 | git shortlog -s -n "$since.." \ 16 | | cut -f 2- \ 17 | | sort -u \ 18 | | grep -v bors\-servo \ 19 | | xargs -d '\n' -I{} echo "- {}" 20 | -------------------------------------------------------------------------------- /releases/release-announcement-template.md: -------------------------------------------------------------------------------- 1 | # Announcing `twiggy` $TODO_VERSION 2 | 3 | `twiggy` is a code size profiler for `.wasm` binaries. 4 | 5 | * [GitHub][] 6 | * [crates.io][] 7 | 8 | Upgrade to this release by reinstalling with `cargo`: 9 | 10 | cargo install -f twiggy 11 | 12 | ## Changelog 13 | 14 | 15 | 16 | ## Friends 17 | 18 | Thanks to everyone who contributed to this release! 19 | 20 | 21 | 22 | ## Contributing 23 | 24 | Want to join us? Check out our [`CONTRIBUTING.md`][contributing] and take a look 25 | at some of these issues: 26 | 27 | * [Issues labeled "good first issue"][good-first-issue] 28 | * [Issues labeled "help wanted"][help-wanted] 29 | 30 | [GitHub]: https://github.com/rustwasm/twiggy 31 | [crates.io]: https://crates.io/crates/twiggy 32 | [guide]: https://rustwasm.github.io/twiggy 33 | [contributing]: https://github.com/rustwasm/twiggy/blob/master/CONTRIBUTING.md 34 | [good-first-issue]: https://github.com/rustwasm/twiggy/labels/good%20first%20issue 35 | [help-wanted]: https://github.com/rustwasm/twiggy/labels/help%20wanted 36 | -------------------------------------------------------------------------------- /traits/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Nick Fitzgerald "] 3 | categories = [] 4 | description = "Common traits for the Twiggy code size profiler." 5 | license = "Apache-2.0/MIT" 6 | name = "twiggy-traits" 7 | readme = "../README.md" 8 | repository = "https://github.com/rustwasm/twiggy" 9 | version = "0.7.0" 10 | edition = "2018" 11 | 12 | [lib] 13 | path = "./traits.rs" 14 | 15 | [dependencies] 16 | thiserror = "1.0" 17 | anyhow = "1.0" 18 | wasmparser = "0.80.0" 19 | twiggy-ir = { version = "=0.7.0", path = "../ir" } 20 | csv = "1.2.2" 21 | regex = "1.4.2" 22 | 23 | [features] 24 | default = [] 25 | dwarf = [] 26 | emit_json = [] 27 | emit_csv = [] 28 | emit_text = [] 29 | -------------------------------------------------------------------------------- /traits/traits.rs: -------------------------------------------------------------------------------- 1 | //! Common traits and types used throughout all of `twiggy`. 2 | #![deny(missing_docs)] 3 | #![deny(missing_debug_implementations)] 4 | 5 | use anyhow::anyhow; 6 | use std::io; 7 | use std::str::FromStr; 8 | use twiggy_ir as ir; 9 | 10 | /// An analysis takes our IR and returns some kind of data results that can be 11 | /// emitted. 12 | pub trait Analyze { 13 | /// The resulting data from this analysis. 14 | type Data: Emit; 15 | 16 | /// Run this analysis on the given IR items. 17 | fn analyze(items: &mut ir::Items) -> anyhow::Result; 18 | } 19 | 20 | /// Selects the parse mode for the input data. 21 | #[derive(Clone, Copy, Debug)] 22 | pub enum ParseMode { 23 | /// WebAssembly file parse mode. 24 | Wasm, 25 | /// DWARF sections parse mode. 26 | #[cfg(feature = "dwarf")] 27 | Dwarf, 28 | /// Automatically determined mode of parsing, e.g. based on file extension. 29 | Auto, 30 | } 31 | 32 | impl Default for ParseMode { 33 | fn default() -> ParseMode { 34 | ParseMode::Auto 35 | } 36 | } 37 | 38 | impl FromStr for ParseMode { 39 | type Err = anyhow::Error; 40 | 41 | fn from_str(s: &str) -> anyhow::Result { 42 | match s { 43 | "wasm" => Ok(ParseMode::Wasm), 44 | #[cfg(feature = "dwarf")] 45 | "dwarf" => Ok(ParseMode::Dwarf), 46 | "auto" => Ok(ParseMode::Auto), 47 | _ => Err(anyhow!("Unknown parse mode: {}", s)), 48 | } 49 | } 50 | } 51 | 52 | /// The format of the output. 53 | #[derive(Clone, Copy, Debug)] 54 | pub enum OutputFormat { 55 | /// Human readable text. 56 | #[cfg(feature = "emit_text")] 57 | Text, 58 | 59 | // /// Hyper Text Markup Language. 60 | // Html, 61 | // /// Graphviz dot format. 62 | // Dot, 63 | /// Comma-separated values (CSV) format. 64 | #[cfg(feature = "emit_csv")] 65 | Csv, 66 | 67 | /// JavaScript Object Notation format. 68 | #[cfg(feature = "emit_json")] 69 | Json, 70 | } 71 | 72 | #[cfg(feature = "emit_text")] 73 | #[cfg(feature = "emit_csv")] 74 | #[cfg(feature = "emit_json")] 75 | impl Default for OutputFormat { 76 | fn default() -> OutputFormat { 77 | OutputFormat::Text 78 | } 79 | } 80 | 81 | impl FromStr for OutputFormat { 82 | type Err = anyhow::Error; 83 | 84 | fn from_str(s: &str) -> Result { 85 | match s { 86 | #[cfg(feature = "emit_text")] 87 | "text" => Ok(OutputFormat::Text), 88 | #[cfg(feature = "emit_json")] 89 | "json" => Ok(OutputFormat::Json), 90 | #[cfg(feature = "emit_csv")] 91 | "csv" => Ok(OutputFormat::Csv), 92 | _ => Err(anyhow!("Unknown output format: {}", s)), 93 | } 94 | } 95 | } 96 | 97 | /// Anything that can write itself in the given output format to the given 98 | /// destination. 99 | pub trait Emit { 100 | /// Emit this thing to the given destination in the given output format. 101 | fn emit( 102 | &self, 103 | items: &ir::Items, 104 | destination: &mut dyn io::Write, 105 | format: OutputFormat, 106 | ) -> anyhow::Result<()> { 107 | match format { 108 | #[cfg(feature = "emit_text")] 109 | OutputFormat::Text => self.emit_text(items, destination), 110 | // OutputFormat::Html => self.emit_html(destination), 111 | // OutputFormat::Dot => self.emit_dot(destination), 112 | #[cfg(feature = "emit_csv")] 113 | OutputFormat::Csv => self.emit_csv(items, destination), 114 | #[cfg(feature = "emit_json")] 115 | OutputFormat::Json => self.emit_json(items, destination), 116 | } 117 | } 118 | 119 | /// Emit human readable text. 120 | #[cfg(feature = "emit_text")] 121 | fn emit_text(&self, items: &ir::Items, destination: &mut dyn io::Write) -> anyhow::Result<()>; 122 | 123 | // /// Emit HTML. 124 | // fn emit_html(&self, destination: &mut dyn io::Write) -> Result<(), Error>; 125 | 126 | // /// Emit Graphviz's dot format. 127 | // fn emit_dot(&self, destination: &mut dyn io::Write) -> Result<(), Error>; 128 | 129 | /// Emit CSV. 130 | #[cfg(feature = "emit_csv")] 131 | fn emit_csv(&self, items: &ir::Items, destination: &mut dyn io::Write) -> anyhow::Result<()>; 132 | 133 | /// Emit JSON. 134 | #[cfg(feature = "emit_json")] 135 | fn emit_json(&self, items: &ir::Items, destination: &mut dyn io::Write) -> anyhow::Result<()>; 136 | } 137 | -------------------------------------------------------------------------------- /twiggy/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Nick Fitzgerald "] 3 | categories = ["command-line-utilities", "development-tools", "development-tools::profiling", "wasm"] 4 | description = "Twiggy is a code size profiler." 5 | license = "Apache-2.0/MIT" 6 | name = "twiggy" 7 | readme = "../README.md" 8 | repository = "https://github.com/rustwasm/twiggy" 9 | version = "0.7.0" 10 | edition = "2018" 11 | 12 | [badges] 13 | travis-ci = { repository = "rustwasm/twiggy" } 14 | 15 | [[bin]] 16 | name = "twiggy" 17 | path = "./twiggy.rs" 18 | 19 | [dependencies] 20 | anyhow = "1.0" 21 | structopt = "0.3" 22 | twiggy-analyze = { version = "=0.7.0", path = "../analyze" } 23 | twiggy-ir = { version = "=0.7.0", path = "../ir" } 24 | twiggy-opt = { version = "=0.7.0", path = "../opt", features = ["cli"] } 25 | twiggy-parser = { version = "=0.7.0", path = "../parser" } 26 | twiggy-traits = { version = "=0.7.0", path = "../traits" } 27 | 28 | [dev-dependencies] 29 | colored = "2.0.0" 30 | diff = "0.1.13" 31 | -------------------------------------------------------------------------------- /twiggy/tests/all/diff_tests.rs: -------------------------------------------------------------------------------- 1 | test!( 2 | diff_wee_alloc, 3 | "diff", 4 | "./fixtures/wee_alloc.wasm", 5 | "./fixtures/wee_alloc.2.wasm" 6 | ); 7 | 8 | test!( 9 | diff_wee_alloc_top_5, 10 | "diff", 11 | "./fixtures/wee_alloc.wasm", 12 | "./fixtures/wee_alloc.2.wasm", 13 | "-n", 14 | "5" 15 | ); 16 | 17 | test!( 18 | diff_wee_alloc_all, 19 | "diff", 20 | "./fixtures/wee_alloc.wasm", 21 | "./fixtures/wee_alloc.2.wasm", 22 | "-a" 23 | ); 24 | 25 | test!( 26 | diff_wee_alloc_all_json, 27 | "diff", 28 | "./fixtures/wee_alloc.wasm", 29 | "./fixtures/wee_alloc.2.wasm", 30 | "-a", 31 | "-f", 32 | "json" 33 | ); 34 | 35 | test!( 36 | diff_wee_alloc_json, 37 | "diff", 38 | "./fixtures/wee_alloc.wasm", 39 | "./fixtures/wee_alloc.2.wasm", 40 | "-f", 41 | "json" 42 | ); 43 | 44 | test!( 45 | diff_wee_alloc_json_top_5, 46 | "diff", 47 | "./fixtures/wee_alloc.wasm", 48 | "./fixtures/wee_alloc.2.wasm", 49 | "-f", 50 | "json", 51 | "-n", 52 | "5" 53 | ); 54 | 55 | test!( 56 | diff_wee_alloc_csv, 57 | "diff", 58 | "./fixtures/wee_alloc.wasm", 59 | "./fixtures/wee_alloc.2.wasm", 60 | "-f", 61 | "csv" 62 | ); 63 | 64 | test!( 65 | diff_wee_alloc_csv_top_5, 66 | "diff", 67 | "./fixtures/wee_alloc.wasm", 68 | "./fixtures/wee_alloc.2.wasm", 69 | "-f", 70 | "csv", 71 | "-n", 72 | "5" 73 | ); 74 | 75 | test!( 76 | diff_test_regex_wee_alloc, 77 | "diff", 78 | "./fixtures/wee_alloc.wasm", 79 | "./fixtures/wee_alloc.2.wasm", 80 | "--regex", 81 | "(data|type)\\[\\d*\\]" 82 | ); 83 | 84 | test!( 85 | diff_test_exact_wee_alloc, 86 | "diff", 87 | "./fixtures/wee_alloc.wasm", 88 | "./fixtures/wee_alloc.2.wasm", 89 | "hello", 90 | "goodbye" 91 | ); 92 | -------------------------------------------------------------------------------- /twiggy/tests/all/dominators_tests.rs: -------------------------------------------------------------------------------- 1 | test!( 2 | dominators_wee_alloc, 3 | "dominators", 4 | "./fixtures/wee_alloc.wasm" 5 | ); 6 | 7 | test!( 8 | dominators_wee_alloc_json, 9 | "dominators", 10 | "./fixtures/wee_alloc.wasm", 11 | "-f", 12 | "json" 13 | ); 14 | 15 | test!( 16 | dominators_wee_alloc_csv, 17 | "dominators", 18 | "./fixtures/wee_alloc.wasm", 19 | "-f", 20 | "csv" 21 | ); 22 | 23 | test!( 24 | dominators_wee_alloc_with_depth_and_row, 25 | "dominators", 26 | "./fixtures/wee_alloc.wasm", 27 | "-d", 28 | "5", 29 | "-r", 30 | "3" 31 | ); 32 | 33 | test!( 34 | dominators_wee_alloc_subtree, 35 | "dominators", 36 | "./fixtures/wee_alloc.wasm", 37 | "hello" 38 | ); 39 | 40 | test!( 41 | dominators_wee_alloc_subtree_json, 42 | "dominators", 43 | "./fixtures/wee_alloc.wasm", 44 | "-f", 45 | "json", 46 | "hello" 47 | ); 48 | 49 | test!( 50 | dominators_json_prints_multiple_root_items, 51 | "dominators", 52 | "./fixtures/paths_test.wasm", 53 | "-f", 54 | "json", 55 | "--regex", 56 | "called.*" 57 | ); 58 | 59 | test!( 60 | dominators_regex_any_func, 61 | "dominators", 62 | "./fixtures/paths_test.wasm", 63 | "--regex", 64 | "func\\[[0-9]+\\]" 65 | ); 66 | 67 | test!( 68 | dominators_csv_does_not_summarize_garbage_if_all_items_are_reachable, 69 | "dominators", 70 | "./fixtures/paths_test.wasm", 71 | "-f", 72 | "csv" 73 | ); 74 | 75 | test!( 76 | dominators_summarizes_unreachable_items, 77 | "dominators", 78 | "./fixtures/garbage.wasm", 79 | "-d", 80 | "1" 81 | ); 82 | -------------------------------------------------------------------------------- /twiggy/tests/all/elf_format_tests.rs: -------------------------------------------------------------------------------- 1 | test!( 2 | elf_top_25_hello_world_rs, 3 | "top", 4 | "-n", 5 | "25", 6 | "./fixtures/hello_elf" 7 | ); 8 | 9 | test!(elf_top_hello_world_rs, "top", "./fixtures/hello_elf"); 10 | 11 | test!( 12 | elf_paths, 13 | "paths", 14 | "./fixtures/hello_elf", 15 | "addr2line::render_file::h8b2b27d4ac1b7166", 16 | "-d", 17 | "3" //"-f", 18 | //"json" 19 | ); 20 | 21 | test!( 22 | elf_paths2, 23 | "paths", 24 | "./fixtures/hello_elf", 25 | "main", 26 | "-d", 27 | "3" //"-f", 28 | //"json" 29 | ); 30 | 31 | test!( 32 | elf_dominators, 33 | "dominators", 34 | "./fixtures/hello_elf", 35 | "-d", 36 | "3" //"-f", 37 | //"json" 38 | ); 39 | 40 | test!( 41 | elf_dominators2, 42 | "dominators", 43 | "./fixtures/hello_elf", 44 | "main", 45 | "-d", 46 | "3" //"-f", 47 | //"json" 48 | ); 49 | 50 | test!( 51 | elf_dominators3, 52 | "dominators", 53 | "./fixtures/hello_elf", 54 | "rust_panic", 55 | "-d", 56 | "3" //"-f", 57 | //"json" 58 | ); 59 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/cpp_monos: -------------------------------------------------------------------------------- 1 | Apprx. Bloat Bytes │ Apprx. Bloat % │ Bytes │ % │ Monomorphizations 2 | ────────────────────┼────────────────┼───────┼────────┼───────────────────────── 3 | 32 ┊ 6.88% ┊ 48 ┊ 10.32% ┊ void generic 4 | ┊ ┊ 16 ┊ 3.44% ┊ void generic() 5 | ┊ ┊ 16 ┊ 3.44% ┊ void generic() 6 | ┊ ┊ 16 ┊ 3.44% ┊ void generic() 7 | 32 ┊ 6.88% ┊ 48 ┊ 10.32% ┊ Σ [4 Total Rows] 8 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/diff_test_exact_wee_alloc: -------------------------------------------------------------------------------- 1 | Delta Bytes │ Item 2 | ─────────────┼────────────────── 3 | +243 ┊ goodbye 4 | +15 ┊ hello 5 | +258 ┊ Σ [2 Total Rows] 6 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/diff_test_regex_wee_alloc: -------------------------------------------------------------------------------- 1 | Delta Bytes │ Item 2 | ─────────────┼────────────────────────────────────────── 3 | -1034 ┊ data[3] 4 | -25 ┊ data[1] 5 | -25 ┊ data[2] 6 | -8 ┊ type[4]: (i32, i32, i32, i32, i32) -> nil 7 | -6 ┊ type[0]: (i32, i32, i32) -> nil 8 | -6 ┊ type[1]: (i32, i32) -> i32 9 | +5 ┊ type[1]: (i32) -> i32 10 | +4 ┊ type[0]: () -> i32 11 | -4 ┊ type[5]: () -> i32 12 | +2 ┊ data[0] 13 | -1097 ┊ Σ [10 Total Rows] 14 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/diff_wee_alloc: -------------------------------------------------------------------------------- 1 | Delta Bytes │ Item 2 | ─────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 3 | -1034 ┊ data[3] 4 | -593 ┊ "function names" subsection 5 | +396 ┊ wee_alloc::alloc_first_fit::he2a4ddf96981c0ce 6 | +243 ┊ goodbye 7 | -226 ┊ wee_alloc::alloc_first_fit::h9a72de3af77ef93f 8 | -153 ┊ wee_alloc::alloc_with_refill::hb32c1bbce9ebda8e 9 | +146 ┊ >::remove::hc9e5d4284e8233b8 10 | -137 ┊ as wee_alloc::AllocPolicy>::new_cell_for_free_list::h3987e3054b8224e6 11 | -77 ┊ ::new_cell_for_free_list::h8f071b7bce0301ba 12 | -25 ┊ data[1] 13 | -25 ┊ data[2] 14 | +15 ┊ hello 15 | +15 ┊ import env::rust_oom 16 | -12 ┊ elem[0] 17 | +10 ┊ custom section 'linking' headers 18 | +8 ┊ global[0] 19 | -8 ┊ type[4]: (i32, i32, i32, i32, i32) -> nil 20 | -7 ┊ ::min_cell_size::hc7cee2a550987099 21 | +7 ┊ alloc::alloc::oom::h45ae3f22a516fb04 22 | -6 ┊ as wee_alloc::AllocPolicy>::min_cell_size::h6f746be886573355 23 | -13 ┊ ... and 14 more. 24 | -1476 ┊ Σ [34 Total Rows] 25 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/diff_wee_alloc_all: -------------------------------------------------------------------------------- 1 | Delta Bytes │ Item 2 | ─────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 3 | -1034 ┊ data[3] 4 | -593 ┊ "function names" subsection 5 | +396 ┊ wee_alloc::alloc_first_fit::he2a4ddf96981c0ce 6 | +243 ┊ goodbye 7 | -226 ┊ wee_alloc::alloc_first_fit::h9a72de3af77ef93f 8 | -153 ┊ wee_alloc::alloc_with_refill::hb32c1bbce9ebda8e 9 | +146 ┊ >::remove::hc9e5d4284e8233b8 10 | -137 ┊ as wee_alloc::AllocPolicy>::new_cell_for_free_list::h3987e3054b8224e6 11 | -77 ┊ ::new_cell_for_free_list::h8f071b7bce0301ba 12 | -25 ┊ data[1] 13 | -25 ┊ data[2] 14 | +15 ┊ hello 15 | +15 ┊ import env::rust_oom 16 | -12 ┊ elem[0] 17 | +10 ┊ custom section 'linking' headers 18 | +8 ┊ global[0] 19 | -8 ┊ type[4]: (i32, i32, i32, i32, i32) -> nil 20 | -7 ┊ ::min_cell_size::hc7cee2a550987099 21 | +7 ┊ alloc::alloc::oom::h45ae3f22a516fb04 22 | -6 ┊ as wee_alloc::AllocPolicy>::min_cell_size::h6f746be886573355 23 | -6 ┊ type[0]: (i32, i32, i32) -> nil 24 | -6 ┊ type[1]: (i32, i32) -> i32 25 | -5 ┊ __wasm_nullptr 26 | +5 ┊ type[1]: (i32) -> i32 27 | -4 ┊ core::ptr::drop_in_place::h4e5cdfd7b9310648.18 28 | -4 ┊ core::ptr::drop_in_place::h8e9fdc2437d43666 29 | +4 ┊ type[0]: () -> i32 30 | -4 ┊ type[5]: () -> i32 31 | +3 ┊ custom section 'linking' 32 | -3 ┊ element section headers 33 | +3 ┊ global section headers 34 | +3 ┊ import section headers 35 | +2 ┊ data[0] 36 | -1 ┊ data section headers 37 | -1476 ┊ Σ [34 Total Rows] 38 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/diff_wee_alloc_all_json: -------------------------------------------------------------------------------- 1 | [{"delta_bytes":-1034,"name":"data[3]"},{"delta_bytes":-593,"name":"\"function names\" subsection"},{"delta_bytes":396,"name":"wee_alloc::alloc_first_fit::he2a4ddf96981c0ce"},{"delta_bytes":243,"name":"goodbye"},{"delta_bytes":-226,"name":"wee_alloc::alloc_first_fit::h9a72de3af77ef93f"},{"delta_bytes":-153,"name":"wee_alloc::alloc_with_refill::hb32c1bbce9ebda8e"},{"delta_bytes":146,"name":">::remove::hc9e5d4284e8233b8"},{"delta_bytes":-137,"name":" as wee_alloc::AllocPolicy>::new_cell_for_free_list::h3987e3054b8224e6"},{"delta_bytes":-77,"name":"::new_cell_for_free_list::h8f071b7bce0301ba"},{"delta_bytes":-25,"name":"data[1]"},{"delta_bytes":-25,"name":"data[2]"},{"delta_bytes":15,"name":"hello"},{"delta_bytes":15,"name":"import env::rust_oom"},{"delta_bytes":-12,"name":"elem[0]"},{"delta_bytes":10,"name":"custom section 'linking' headers"},{"delta_bytes":8,"name":"global[0]"},{"delta_bytes":-8,"name":"type[4]: (i32, i32, i32, i32, i32) -> nil"},{"delta_bytes":-7,"name":"::min_cell_size::hc7cee2a550987099"},{"delta_bytes":7,"name":"alloc::alloc::oom::h45ae3f22a516fb04"},{"delta_bytes":-6,"name":" as wee_alloc::AllocPolicy>::min_cell_size::h6f746be886573355"},{"delta_bytes":-6,"name":"type[0]: (i32, i32, i32) -> nil"},{"delta_bytes":-6,"name":"type[1]: (i32, i32) -> i32"},{"delta_bytes":-5,"name":"__wasm_nullptr"},{"delta_bytes":5,"name":"type[1]: (i32) -> i32"},{"delta_bytes":-4,"name":"core::ptr::drop_in_place::h4e5cdfd7b9310648.18"},{"delta_bytes":-4,"name":"core::ptr::drop_in_place::h8e9fdc2437d43666"},{"delta_bytes":4,"name":"type[0]: () -> i32"},{"delta_bytes":-4,"name":"type[5]: () -> i32"},{"delta_bytes":3,"name":"custom section 'linking'"},{"delta_bytes":-3,"name":"element section headers"},{"delta_bytes":3,"name":"global section headers"},{"delta_bytes":3,"name":"import section headers"},{"delta_bytes":2,"name":"data[0]"},{"delta_bytes":-1,"name":"data section headers"},{"delta_bytes":-1476,"name":"Σ [34 Total Rows]"}] -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/diff_wee_alloc_csv: -------------------------------------------------------------------------------- 1 | DeltaBytes,Item 2 | -1034,data[3] 3 | -593,"""function names"" subsection" 4 | +396,wee_alloc::alloc_first_fit::he2a4ddf96981c0ce 5 | +243,goodbye 6 | -226,wee_alloc::alloc_first_fit::h9a72de3af77ef93f 7 | -153,wee_alloc::alloc_with_refill::hb32c1bbce9ebda8e 8 | +146,">::remove::hc9e5d4284e8233b8" 9 | -137, as wee_alloc::AllocPolicy>::new_cell_for_free_list::h3987e3054b8224e6 10 | -77,::new_cell_for_free_list::h8f071b7bce0301ba 11 | -25,data[1] 12 | -25,data[2] 13 | +15,hello 14 | +15,import env::rust_oom 15 | -12,elem[0] 16 | +10,custom section 'linking' headers 17 | +8,global[0] 18 | -8,"type[4]: (i32, i32, i32, i32, i32) -> nil" 19 | -7,::min_cell_size::hc7cee2a550987099 20 | +7,alloc::alloc::oom::h45ae3f22a516fb04 21 | -6, as wee_alloc::AllocPolicy>::min_cell_size::h6f746be886573355 22 | -13,... and 14 more. 23 | -1476,Σ [34 Total Rows] 24 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/diff_wee_alloc_csv_top_5: -------------------------------------------------------------------------------- 1 | DeltaBytes,Item 2 | -1034,data[3] 3 | -593,"""function names"" subsection" 4 | +396,wee_alloc::alloc_first_fit::he2a4ddf96981c0ce 5 | +243,goodbye 6 | -226,wee_alloc::alloc_first_fit::h9a72de3af77ef93f 7 | -262,... and 29 more. 8 | -1476,Σ [34 Total Rows] 9 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/diff_wee_alloc_json: -------------------------------------------------------------------------------- 1 | [{"delta_bytes":-1034,"name":"data[3]"},{"delta_bytes":-593,"name":"\"function names\" subsection"},{"delta_bytes":396,"name":"wee_alloc::alloc_first_fit::he2a4ddf96981c0ce"},{"delta_bytes":243,"name":"goodbye"},{"delta_bytes":-226,"name":"wee_alloc::alloc_first_fit::h9a72de3af77ef93f"},{"delta_bytes":-153,"name":"wee_alloc::alloc_with_refill::hb32c1bbce9ebda8e"},{"delta_bytes":146,"name":">::remove::hc9e5d4284e8233b8"},{"delta_bytes":-137,"name":" as wee_alloc::AllocPolicy>::new_cell_for_free_list::h3987e3054b8224e6"},{"delta_bytes":-77,"name":"::new_cell_for_free_list::h8f071b7bce0301ba"},{"delta_bytes":-25,"name":"data[1]"},{"delta_bytes":-25,"name":"data[2]"},{"delta_bytes":15,"name":"hello"},{"delta_bytes":15,"name":"import env::rust_oom"},{"delta_bytes":-12,"name":"elem[0]"},{"delta_bytes":10,"name":"custom section 'linking' headers"},{"delta_bytes":8,"name":"global[0]"},{"delta_bytes":-8,"name":"type[4]: (i32, i32, i32, i32, i32) -> nil"},{"delta_bytes":-7,"name":"::min_cell_size::hc7cee2a550987099"},{"delta_bytes":7,"name":"alloc::alloc::oom::h45ae3f22a516fb04"},{"delta_bytes":-6,"name":" as wee_alloc::AllocPolicy>::min_cell_size::h6f746be886573355"},{"delta_bytes":-13,"name":"... and 14 more."},{"delta_bytes":-1476,"name":"Σ [34 Total Rows]"}] -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/diff_wee_alloc_json_top_5: -------------------------------------------------------------------------------- 1 | [{"delta_bytes":-1034,"name":"data[3]"},{"delta_bytes":-593,"name":"\"function names\" subsection"},{"delta_bytes":396,"name":"wee_alloc::alloc_first_fit::he2a4ddf96981c0ce"},{"delta_bytes":243,"name":"goodbye"},{"delta_bytes":-226,"name":"wee_alloc::alloc_first_fit::h9a72de3af77ef93f"},{"delta_bytes":-262,"name":"... and 29 more."},{"delta_bytes":-1476,"name":"Σ [34 Total Rows]"}] -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/diff_wee_alloc_top_5: -------------------------------------------------------------------------------- 1 | Delta Bytes │ Item 2 | ─────────────┼────────────────────────────────────────────── 3 | -1034 ┊ data[3] 4 | -593 ┊ "function names" subsection 5 | +396 ┊ wee_alloc::alloc_first_fit::he2a4ddf96981c0ce 6 | +243 ┊ goodbye 7 | -226 ┊ wee_alloc::alloc_first_fit::h9a72de3af77ef93f 8 | -262 ┊ ... and 29 more. 9 | -1476 ┊ Σ [34 Total Rows] 10 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/dominators_csv_does_not_summarize_garbage_if_all_items_are_reachable: -------------------------------------------------------------------------------- 1 | Id,Name,ShallowSize,ShallowSizePercent,RetainedSize,RetainedSizePercent,ImmediateDominator 2 | 18446744073709551615,,0,0.0,144,100.0,18446744073709551615 3 | 42949672960,"""function names"" subsection",46,31.944444444444443,46,31.944444444444443,18446744073709551615 4 | 12884901890,"export ""woof""",7,4.861111111111112,22,15.277777777777779,18446744073709551615 5 | 17179869187,woof,9,6.25,15,10.416666666666668,12884901890 6 | 17179869184,calledOnce,6,4.166666666666666,6,4.166666666666666,17179869187 7 | 12884901888,"export ""awoo""",7,4.861111111111112,13,9.027777777777777,18446744073709551615 8 | 17179869188,awoo,6,4.166666666666666,6,4.166666666666666,12884901888 9 | 42949672961,"""local names"" subsection",13,9.027777777777777,13,9.027777777777777,18446744073709551615 10 | 4294967295,wasm magic bytes,8,5.555555555555555,8,5.555555555555555,18446744073709551615 11 | 12884901889,"export ""bark""",7,4.861111111111112,7,4.861111111111112,18446744073709551615 12 | 47244640255,custom section 'name' headers,7,4.861111111111112,7,4.861111111111112,18446744073709551615 13 | 17179869185,calledTwice,6,4.166666666666666,6,4.166666666666666,18446744073709551615 14 | 17179869186,bark,6,4.166666666666666,6,4.166666666666666,18446744073709551615 15 | 21474836479,code section headers,6,4.166666666666666,6,4.166666666666666,18446744073709551615 16 | 4294967296,type[0]: () -> i32,4,2.7777777777777777,4,2.7777777777777777,18446744073709551615 17 | 8589934591,type section headers,3,2.083333333333333,3,2.083333333333333,18446744073709551615 18 | 17179869183,export section headers,3,2.083333333333333,3,2.083333333333333,18446744073709551615 19 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/dominators_json_prints_multiple_root_items: -------------------------------------------------------------------------------- 1 | {"items":[{"name":"calledOnce","shallow_size":6,"shallow_size_percent":4.166666666666666,"retained_size":6,"retained_size_percent":4.166666666666666},{"name":"calledTwice","shallow_size":6,"shallow_size_percent":4.166666666666666,"retained_size":6,"retained_size_percent":4.166666666666666}]} -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/dominators_regex_any_func: -------------------------------------------------------------------------------- 1 | Retained Bytes │ Retained % │ Dominator Tree 2 | ────────────────┼────────────┼─────────────── 3 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/dominators_summarizes_unreachable_items: -------------------------------------------------------------------------------- 1 | Retained Bytes │ Retained % │ Dominator Tree 2 | ────────────────┼────────────┼────────────────────────────── 3 | 58 ┊ 29.44% ┊ "function names" subsection 4 | 48 ┊ 24.37% ┊ "local names" subsection 5 | 21 ┊ 10.66% ┊ export "add" 6 | 8 ┊ 4.06% ┊ wasm magic bytes 7 | 7 ┊ 3.55% ┊ custom section 'name' headers 8 | 6 ┊ 3.05% ┊ code section headers 9 | 3 ┊ 1.52% ┊ type section headers 10 | 3 ┊ 1.52% ┊ export section headers 11 | 43 ┊ 21.83% ┊ [6 Unreachable Items] 12 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/dominators_wee_alloc: -------------------------------------------------------------------------------- 1 | Retained Bytes │ Retained % │ Dominator Tree 2 | ────────────────┼────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 3 | 777 ┊ 27.58% ┊ "function names" subsection 4 | 387 ┊ 13.74% ┊ wee_alloc::alloc_with_refill::hb32c1bbce9ebda8e 5 | 226 ┊ 8.02% ┊ ⤷ wee_alloc::alloc_first_fit::h9a72de3af77ef93f 6 | 8 ┊ 0.28% ┊ ⤷ type[4]: (i32, i32, i32, i32, i32) -> nil 7 | 271 ┊ 9.62% ┊ table[0] 8 | 267 ┊ 9.48% ┊ ⤷ elem[0] 9 | 137 ┊ 4.86% ┊ ⤷ as wee_alloc::AllocPolicy>::new_cell_for_free_list::h3987e3054b8224e6 10 | 77 ┊ 2.73% ┊ ⤷ ::new_cell_for_free_list::h8f071b7bce0301ba 11 | 8 ┊ 0.28% ┊ ⤷ __wasm_nullptr 12 | 3 ┊ 0.11% ┊ ⤷ type[2]: () -> nil 13 | 7 ┊ 0.25% ┊ ⤷ ::min_cell_size::hc7cee2a550987099 14 | 6 ┊ 0.21% ┊ ⤷ type[0]: (i32, i32, i32) -> nil 15 | 6 ┊ 0.21% ┊ ⤷ type[1]: (i32, i32) -> i32 16 | 6 ┊ 0.21% ┊ ⤷ as wee_alloc::AllocPolicy>::min_cell_size::h6f746be886573355 17 | 4 ┊ 0.14% ┊ ⤷ core::ptr::drop_in_place::h8e9fdc2437d43666 18 | 4 ┊ 0.14% ┊ ⤷ core::ptr::drop_in_place::h4e5cdfd7b9310648.18 19 | 177 ┊ 6.28% ┊ export "hello" 20 | 169 ┊ 6.00% ┊ ⤷ hello 21 | 4 ┊ 0.14% ┊ ⤷ type[5]: () -> i32 22 | 55 ┊ 1.95% ┊ export "goodbye" 23 | 45 ┊ 1.60% ┊ ⤷ goodbye 24 | 11 ┊ 0.39% ┊ export "memory" 25 | 2 ┊ 0.07% ┊ ⤷ memory[0] 26 | 9 ┊ 0.32% ┊ data[0] 27 | 8 ┊ 0.28% ┊ wasm magic bytes 28 | 8 ┊ 0.28% ┊ custom section 'name' headers 29 | 7 ┊ 0.25% ┊ code section headers 30 | 4 ┊ 0.14% ┊ type[3]: (i32) -> nil 31 | 4 ┊ 0.14% ┊ data section headers 32 | 3 ┊ 0.11% ┊ type section headers 33 | 3 ┊ 0.11% ┊ table section headers 34 | 3 ┊ 0.11% ┊ memory section headers 35 | 3 ┊ 0.11% ┊ export section headers 36 | 3 ┊ 0.11% ┊ element section headers 37 | 1084 ┊ 38.48% ┊ [3 Unreachable Items] 38 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/dominators_wee_alloc_csv: -------------------------------------------------------------------------------- 1 | Id,Name,ShallowSize,ShallowSizePercent,RetainedSize,RetainedSizePercent,ImmediateDominator 2 | 18446744073709551615,,0,0.0,1733,61.519346822861195,18446744073709551615 3 | 85899345920,"""function names"" subsection",777,27.582534611288605,777,27.582534611288605,18446744073709551615 4 | 30064771074,wee_alloc::alloc_with_refill::hb32c1bbce9ebda8e,153,5.431309904153355,387,13.738019169329075,18446744073709551615 5 | 30064771075,wee_alloc::alloc_first_fit::h9a72de3af77ef93f,226,8.022719204827832,226,8.022719204827832,30064771074 6 | 4294967300,"type[4]: (i32, i32, i32, i32, i32) -> nil",8,0.2839900603478878,8,0.2839900603478878,30064771074 7 | 12884901888,table[0],4,0.1419950301739439,271,9.620163294284701,18446744073709551615 8 | 25769803776,elem[0],12,0.42598509052183176,267,9.478168264110757,12884901888 9 | 30064771077, as wee_alloc::AllocPolicy>::new_cell_for_free_list::h3987e3054b8224e6,137,4.863329783457579,137,4.863329783457579,25769803776 10 | 30064771072,::new_cell_for_free_list::h8f071b7bce0301ba,77,2.7334043308484204,77,2.7334043308484204,25769803776 11 | 30064771082,__wasm_nullptr,5,0.1774937877174299,8,0.2839900603478878,25769803776 12 | 4294967298,type[2]: () -> nil,3,0.10649627263045794,3,0.10649627263045794,30064771082 13 | 30064771073,::min_cell_size::hc7cee2a550987099,7,0.24849130280440185,7,0.24849130280440185,25769803776 14 | 4294967296,"type[0]: (i32, i32, i32) -> nil",6,0.21299254526091588,6,0.21299254526091588,25769803776 15 | 4294967297,"type[1]: (i32, i32) -> i32",6,0.21299254526091588,6,0.21299254526091588,25769803776 16 | 30064771079, as wee_alloc::AllocPolicy>::min_cell_size::h6f746be886573355,6,0.21299254526091588,6,0.21299254526091588,25769803776 17 | 30064771076,core::ptr::drop_in_place::h8e9fdc2437d43666,4,0.1419950301739439,4,0.1419950301739439,25769803776 18 | 30064771078,core::ptr::drop_in_place::h4e5cdfd7b9310648.18,4,0.1419950301739439,4,0.1419950301739439,25769803776 19 | 21474836481,"export ""hello""",8,0.2839900603478878,177,6.283280085197019,18446744073709551615 20 | 30064771080,hello,165,5.857294994675186,169,5.999290024849131,21474836481 21 | 4294967301,type[5]: () -> i32,4,0.1419950301739439,4,0.1419950301739439,30064771080 22 | 21474836482,"export ""goodbye""",10,0.3549875754348598,55,1.9524316648917288,18446744073709551615 23 | 30064771081,goodbye,45,1.5974440894568689,45,1.5974440894568689,21474836482 24 | 21474836480,"export ""memory""",9,0.3194888178913738,11,0.3904863329783458,18446744073709551615 25 | 17179869184,memory[0],2,0.07099751508697195,2,0.07099751508697195,21474836480 26 | 81604378624,data[0],9,0.3194888178913738,9,0.3194888178913738,18446744073709551615 27 | 4294967295,wasm magic bytes,8,0.2839900603478878,8,0.2839900603478878,18446744073709551615 28 | 90194313215,custom section 'name' headers,8,0.2839900603478878,8,0.2839900603478878,18446744073709551615 29 | 34359738367,code section headers,7,0.24849130280440185,7,0.24849130280440185,18446744073709551615 30 | 4294967299,type[3]: (i32) -> nil,4,0.1419950301739439,4,0.1419950301739439,18446744073709551615 31 | 85899345919,data section headers,4,0.1419950301739439,4,0.1419950301739439,18446744073709551615 32 | 8589934591,type section headers,3,0.10649627263045794,3,0.10649627263045794,18446744073709551615 33 | 17179869183,table section headers,3,0.10649627263045794,3,0.10649627263045794,18446744073709551615 34 | 21474836479,memory section headers,3,0.10649627263045794,3,0.10649627263045794,18446744073709551615 35 | 25769803775,export section headers,3,0.10649627263045794,3,0.10649627263045794,18446744073709551615 36 | 30064771071,element section headers,3,0.10649627263045794,3,0.10649627263045794,18446744073709551615 37 | ,[3 Unreachable Items],1084,38.480653177138805,1084,38.480653177138805, 38 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/dominators_wee_alloc_json: -------------------------------------------------------------------------------- 1 | {"items":[{"name":"","shallow_size":0,"shallow_size_percent":0,"retained_size":1733,"retained_size_percent":61.519346822861195,"children":[{"name":"\"function names\" subsection","shallow_size":777,"shallow_size_percent":27.582534611288605,"retained_size":777,"retained_size_percent":27.582534611288605},{"name":"wee_alloc::alloc_with_refill::hb32c1bbce9ebda8e","shallow_size":153,"shallow_size_percent":5.431309904153355,"retained_size":387,"retained_size_percent":13.738019169329075,"children":[{"name":"wee_alloc::alloc_first_fit::h9a72de3af77ef93f","shallow_size":226,"shallow_size_percent":8.022719204827832,"retained_size":226,"retained_size_percent":8.022719204827832},{"name":"type[4]: (i32, i32, i32, i32, i32) -> nil","shallow_size":8,"shallow_size_percent":0.2839900603478878,"retained_size":8,"retained_size_percent":0.2839900603478878}]},{"name":"table[0]","shallow_size":4,"shallow_size_percent":0.1419950301739439,"retained_size":271,"retained_size_percent":9.620163294284701,"children":[{"name":"elem[0]","shallow_size":12,"shallow_size_percent":0.42598509052183176,"retained_size":267,"retained_size_percent":9.478168264110757,"children":[{"name":" as wee_alloc::AllocPolicy>::new_cell_for_free_list::h3987e3054b8224e6","shallow_size":137,"shallow_size_percent":4.863329783457579,"retained_size":137,"retained_size_percent":4.863329783457579},{"name":"::new_cell_for_free_list::h8f071b7bce0301ba","shallow_size":77,"shallow_size_percent":2.7334043308484204,"retained_size":77,"retained_size_percent":2.7334043308484204},{"name":"__wasm_nullptr","shallow_size":5,"shallow_size_percent":0.1774937877174299,"retained_size":8,"retained_size_percent":0.2839900603478878,"children":[{"name":"type[2]: () -> nil","shallow_size":3,"shallow_size_percent":0.10649627263045794,"retained_size":3,"retained_size_percent":0.10649627263045794}]},{"name":"::min_cell_size::hc7cee2a550987099","shallow_size":7,"shallow_size_percent":0.24849130280440185,"retained_size":7,"retained_size_percent":0.24849130280440185},{"name":"type[0]: (i32, i32, i32) -> nil","shallow_size":6,"shallow_size_percent":0.21299254526091588,"retained_size":6,"retained_size_percent":0.21299254526091588},{"name":"type[1]: (i32, i32) -> i32","shallow_size":6,"shallow_size_percent":0.21299254526091588,"retained_size":6,"retained_size_percent":0.21299254526091588},{"name":" as wee_alloc::AllocPolicy>::min_cell_size::h6f746be886573355","shallow_size":6,"shallow_size_percent":0.21299254526091588,"retained_size":6,"retained_size_percent":0.21299254526091588},{"name":"core::ptr::drop_in_place::h8e9fdc2437d43666","shallow_size":4,"shallow_size_percent":0.1419950301739439,"retained_size":4,"retained_size_percent":0.1419950301739439},{"name":"core::ptr::drop_in_place::h4e5cdfd7b9310648.18","shallow_size":4,"shallow_size_percent":0.1419950301739439,"retained_size":4,"retained_size_percent":0.1419950301739439}]}]},{"name":"export \"hello\"","shallow_size":8,"shallow_size_percent":0.2839900603478878,"retained_size":177,"retained_size_percent":6.283280085197019,"children":[{"name":"hello","shallow_size":165,"shallow_size_percent":5.857294994675186,"retained_size":169,"retained_size_percent":5.999290024849131,"children":[{"name":"type[5]: () -> i32","shallow_size":4,"shallow_size_percent":0.1419950301739439,"retained_size":4,"retained_size_percent":0.1419950301739439}]}]},{"name":"export \"goodbye\"","shallow_size":10,"shallow_size_percent":0.3549875754348598,"retained_size":55,"retained_size_percent":1.9524316648917288,"children":[{"name":"goodbye","shallow_size":45,"shallow_size_percent":1.5974440894568689,"retained_size":45,"retained_size_percent":1.5974440894568689}]},{"name":"export \"memory\"","shallow_size":9,"shallow_size_percent":0.3194888178913738,"retained_size":11,"retained_size_percent":0.3904863329783458,"children":[{"name":"memory[0]","shallow_size":2,"shallow_size_percent":0.07099751508697195,"retained_size":2,"retained_size_percent":0.07099751508697195}]},{"name":"data[0]","shallow_size":9,"shallow_size_percent":0.3194888178913738,"retained_size":9,"retained_size_percent":0.3194888178913738},{"name":"wasm magic bytes","shallow_size":8,"shallow_size_percent":0.2839900603478878,"retained_size":8,"retained_size_percent":0.2839900603478878},{"name":"custom section 'name' headers","shallow_size":8,"shallow_size_percent":0.2839900603478878,"retained_size":8,"retained_size_percent":0.2839900603478878},{"name":"code section headers","shallow_size":7,"shallow_size_percent":0.24849130280440185,"retained_size":7,"retained_size_percent":0.24849130280440185},{"name":"type[3]: (i32) -> nil","shallow_size":4,"shallow_size_percent":0.1419950301739439,"retained_size":4,"retained_size_percent":0.1419950301739439},{"name":"data section headers","shallow_size":4,"shallow_size_percent":0.1419950301739439,"retained_size":4,"retained_size_percent":0.1419950301739439},{"name":"type section headers","shallow_size":3,"shallow_size_percent":0.10649627263045794,"retained_size":3,"retained_size_percent":0.10649627263045794},{"name":"table section headers","shallow_size":3,"shallow_size_percent":0.10649627263045794,"retained_size":3,"retained_size_percent":0.10649627263045794},{"name":"memory section headers","shallow_size":3,"shallow_size_percent":0.10649627263045794,"retained_size":3,"retained_size_percent":0.10649627263045794},{"name":"export section headers","shallow_size":3,"shallow_size_percent":0.10649627263045794,"retained_size":3,"retained_size_percent":0.10649627263045794},{"name":"element section headers","shallow_size":3,"shallow_size_percent":0.10649627263045794,"retained_size":3,"retained_size_percent":0.10649627263045794}]}],"summary":[{"name":"[3 Unreachable Items]","retained_size":1084,"retained_size_percent":38.480653177138805}]} -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/dominators_wee_alloc_subtree: -------------------------------------------------------------------------------- 1 | Retained Bytes │ Retained % │ Dominator Tree 2 | ────────────────┼────────────┼───────────────────────── 3 | 169 ┊ 6.00% ┊ hello 4 | 4 ┊ 0.14% ┊ ⤷ type[5]: () -> i32 5 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/dominators_wee_alloc_subtree_json: -------------------------------------------------------------------------------- 1 | {"items":[{"name":"hello","shallow_size":165,"shallow_size_percent":5.857294994675186,"retained_size":169,"retained_size_percent":5.999290024849131,"children":[{"name":"type[5]: () -> i32","shallow_size":4,"shallow_size_percent":0.1419950301739439,"retained_size":4,"retained_size_percent":0.1419950301739439}]}]} -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/dominators_wee_alloc_with_depth_and_row: -------------------------------------------------------------------------------- 1 | Retained Bytes │ Retained % │ Dominator Tree 2 | ────────────────┼────────────┼──────────────────────────────────────────────────── 3 | 777 ┊ 27.58% ┊ "function names" subsection 4 | 387 ┊ 13.74% ┊ wee_alloc::alloc_with_refill::hb32c1bbce9ebda8e 5 | 226 ┊ 8.02% ┊ ⤷ wee_alloc::alloc_first_fit::h9a72de3af77ef93f 6 | 1084 ┊ 38.48% ┊ [3 Unreachable Items] 7 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/elf_dominators: -------------------------------------------------------------------------------- 1 | Retained Bytes │ Retained % │ Dominator Tree 2 | ────────────────┼────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────── 3 | 20756 ┊ 6.62% ┊ _start 4 | 20722 ┊ 6.61% ┊ ⤷ main 5 | 6787 ┊ 2.16% ┊ ⤷ rust_begin_unwind 6 | 2345 ┊ 0.75% ┊ ⤷ core::fmt::Formatter::pad::hcc5fe622846bf700 7 | 2320 ┊ 0.74% ┊ ⤷ ::fmt::h754a49e4fc8ec8aa 8 | 920 ┊ 0.29% ┊ ⤷ hello_world::main::h566bf36a5559a1df 9 | 904 ┊ 0.29% ┊ ⤷ std::sys::pal::unix::stack_overflow::imp::signal_handler::h92742cf6f22d3519 10 | 718 ┊ 0.23% ┊ ⤷ core::fmt::Formatter::pad_integral::h96d805165c239be7 11 | 546 ┊ 0.17% ┊ ⤷ std::sys::sync::once::futex::Once::call::hba7a0ad2312b9b6c 12 | 510 ┊ 0.16% ┊ ⤷ core::fmt::write::h7ca648217bc79799 13 | 468 ┊ 0.15% ┊ ⤷ std::sys::thread_local::guard::key::enable::hbcc9e853fe020472 14 | 454 ┊ 0.14% ┊ ⤷ std::sys::pal::unix::stack_overflow::imp::make_handler::h9e165333838f87d4 15 | 362 ┊ 0.12% ┊ ⤷ core::panicking::assert_failed::hf5c4898457e9114b 16 | 281 ┊ 0.09% ┊ ⤷ core::fmt::num::imp::::fmt::hb59860de16ff6386 17 | 274 ┊ 0.09% ┊ ⤷ alloc::alloc::handle_alloc_error::hff7cd8bed17d1d5d 18 | 257 ┊ 0.08% ┊ ⤷ std::io::Write::write_fmt::h7960c58bfa5ccbcb 19 | 247 ┊ 0.08% ┊ ⤷ std::sys::sync::mutex::futex::Mutex::lock_contended::h2821e64f818bf249 20 | 229 ┊ 0.07% ┊ ⤷ __rust_panic_cleanup 21 | 137 ┊ 0.04% ┊ ⤷ __rust_realloc 22 | 127 ┊ 0.04% ┊ ⤷ core::ptr::drop_in_place::hef6122dd753b18b9 23 | 88 ┊ 0.03% ┊ ⤷ core::panicking::panic_in_cleanup::h15db6728a9723d70 24 | 72 ┊ 0.02% ┊ ⤷ alloc::sync::Arc::drop_slow::h7fb2849d4e951291 25 | 69 ┊ 0.02% ┊ ⤷ core::panicking::panic_nounwind::h2f7749cb358aa979 26 | 67 ┊ 0.02% ┊ ⤷ core::panicking::panic_nounwind_fmt::h57347130f21a7343 27 | 58 ┊ 0.02% ┊ ⤷ std::thread::ThreadId::new::exhausted::h587e4c0974c27867 28 | 54 ┊ 0.02% ┊ ⤷ core::panicking::panic_const::panic_const_rem_by_zero::h3a1a875e01d18d87 29 | 54 ┊ 0.02% ┊ ⤷ __rust_alloc 30 | 54 ┊ 0.02% ┊ ⤷ alloc::raw_vec::capacity_overflow::h1d367b37f58596d3 31 | 31 ┊ 0.01% ┊ ⤷ core::panicking::panic_fmt::hf8ffc7c15bfb58a0 32 | 23 ┊ 0.01% ┊ ⤷ <&T as core::fmt::Display>::fmt::h0e3ec628b856fe61 33 | 22 ┊ 0.01% ┊ ⤷ alloc::raw_vec::handle_error::he39a50b4f8f417a7 34 | 21 ┊ 0.01% ┊ ⤷ <&T as core::fmt::Debug>::fmt::hdec3cd7df363df61 35 | 19 ┊ 0.01% ┊ ⤷ std::panicking::panic_count::is_zero_slow_path::h964c4711602484bf 36 | 17 ┊ 0.01% ┊ ⤷ core::ptr::drop_in_place>::h9106c28cca74d265 37 | 16 ┊ 0.01% ┊ ⤷ core::panicking::panic_cannot_unwind::hf517234e476fc7c7 38 | 16 ┊ 0.01% ┊ ⤷ .Lanon.a9d7ff4935140b8d38885355bf97d04a.323 39 | 10 ┊ 0.00% ┊ ⤷ std::sys::pal::unix::abort_internal::hac8a9a93523d7e74 40 | 5 ┊ 0.00% ┊ ⤷ std::sys::backtrace::__rust_begin_short_backtrace::hfacc9bc09697879e 41 | 4 ┊ 0.00% ┊ ⤷ .Lanon.a9d7ff4935140b8d38885355bf97d04a.237 42 | 221022 ┊ 70.46% ┊ [343 Unreachable Items] 43 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/elf_dominators2: -------------------------------------------------------------------------------- 1 | Retained Bytes │ Retained % │ Dominator Tree 2 | ────────────────┼────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 3 | 20722 ┊ 6.61% ┊ main 4 | 6787 ┊ 2.16% ┊ ⤷ rust_begin_unwind 5 | 6759 ┊ 2.15% ┊ ⤷ std::sys::backtrace::__rust_end_short_backtrace::h7d7e47ef99abf6aa 6 | 2345 ┊ 0.75% ┊ ⤷ core::fmt::Formatter::pad::hcc5fe622846bf700 7 | 1234 ┊ 0.39% ┊ ⤷ core::str::count::do_count_chars::h8d3bc63d9ea1b16d 8 | 2320 ┊ 0.74% ┊ ⤷ ::fmt::h754a49e4fc8ec8aa 9 | 1259 ┊ 0.40% ┊ ⤷ alloc::string::String::from_utf8_lossy::h93e501670dab1f99 10 | 282 ┊ 0.09% ┊ ⤷ core::fmt::num::imp::::fmt::h658fefd5335c76dd 11 | 24 ┊ 0.01% ┊ ⤷ ::fmt::h29bfab9f1324032c 12 | 920 ┊ 0.29% ┊ ⤷ hello_world::main::h566bf36a5559a1df 13 | 105 ┊ 0.03% ┊ ⤷ core::option::expect_failed::h11ade060093e7060 14 | 59 ┊ 0.02% ┊ ⤷ core::ptr::drop_in_place>>>::h67f2b1dc2580eeeb 15 | 904 ┊ 0.29% ┊ ⤷ std::sys::pal::unix::stack_overflow::imp::signal_handler::h92742cf6f22d3519 16 | 433 ┊ 0.14% ┊ ⤷ std::thread::current::current::h6e1ac74d3b8c367c 17 | 32 ┊ 0.01% ┊ ⤷ core::ptr::drop_in_place::h203009a4ad36bada 18 | 718 ┊ 0.23% ┊ ⤷ core::fmt::Formatter::pad_integral::h96d805165c239be7 19 | 84 ┊ 0.03% ┊ ⤷ core::fmt::Formatter::pad_integral::write_prefix::hd30895a40eef31e8 20 | 546 ┊ 0.17% ┊ ⤷ std::sys::sync::once::futex::Once::call::hba7a0ad2312b9b6c 21 | 510 ┊ 0.16% ┊ ⤷ core::fmt::write::h7ca648217bc79799 22 | 468 ┊ 0.15% ┊ ⤷ std::sys::thread_local::guard::key::enable::hbcc9e853fe020472 23 | 234 ┊ 0.07% ┊ ⤷ std::sys::thread_local::guard::key::enable::DTORS::h2a503f494e7daf42 24 | 454 ┊ 0.14% ┊ ⤷ std::sys::pal::unix::stack_overflow::imp::make_handler::h9e165333838f87d4 25 | 362 ┊ 0.12% ┊ ⤷ core::panicking::assert_failed::hf5c4898457e9114b 26 | 315 ┊ 0.10% ┊ ⤷ core::panicking::assert_failed_inner::h4797f91cd349a0eb 27 | 4 ┊ 0.00% ┊ ⤷ .Lanon.a9d7ff4935140b8d38885355bf97d04a.868 28 | 281 ┊ 0.09% ┊ ⤷ core::fmt::num::imp::::fmt::hb59860de16ff6386 29 | 274 ┊ 0.09% ┊ ⤷ alloc::alloc::handle_alloc_error::hff7cd8bed17d1d5d 30 | 256 ┊ 0.08% ┊ ⤷ __rust_alloc_error_handler 31 | 257 ┊ 0.08% ┊ ⤷ std::io::Write::write_fmt::h7960c58bfa5ccbcb 32 | 247 ┊ 0.08% ┊ ⤷ std::sys::sync::mutex::futex::Mutex::lock_contended::h2821e64f818bf249 33 | 229 ┊ 0.07% ┊ ⤷ __rust_panic_cleanup 34 | 158 ┊ 0.05% ┊ ⤷ __rust_foreign_exception 35 | 137 ┊ 0.04% ┊ ⤷ __rust_realloc 36 | 127 ┊ 0.04% ┊ ⤷ core::ptr::drop_in_place::hef6122dd753b18b9 37 | 88 ┊ 0.03% ┊ ⤷ core::panicking::panic_in_cleanup::h15db6728a9723d70 38 | 79 ┊ 0.03% ┊ ⤷ core::panicking::panic_nounwind_nobacktrace::hdf7fac9038a56f32 39 | 72 ┊ 0.02% ┊ ⤷ alloc::sync::Arc::drop_slow::h7fb2849d4e951291 40 | 69 ┊ 0.02% ┊ ⤷ core::panicking::panic_nounwind::h2f7749cb358aa979 41 | 67 ┊ 0.02% ┊ ⤷ core::panicking::panic_nounwind_fmt::h57347130f21a7343 42 | 58 ┊ 0.02% ┊ ⤷ std::thread::ThreadId::new::exhausted::h587e4c0974c27867 43 | 54 ┊ 0.02% ┊ ⤷ core::panicking::panic_const::panic_const_rem_by_zero::h3a1a875e01d18d87 44 | 54 ┊ 0.02% ┊ ⤷ __rust_alloc 45 | 54 ┊ 0.02% ┊ ⤷ alloc::raw_vec::capacity_overflow::h1d367b37f58596d3 46 | 31 ┊ 0.01% ┊ ⤷ core::panicking::panic_fmt::hf8ffc7c15bfb58a0 47 | 23 ┊ 0.01% ┊ ⤷ <&T as core::fmt::Display>::fmt::h0e3ec628b856fe61 48 | 22 ┊ 0.01% ┊ ⤷ alloc::raw_vec::handle_error::he39a50b4f8f417a7 49 | 21 ┊ 0.01% ┊ ⤷ <&T as core::fmt::Debug>::fmt::hdec3cd7df363df61 50 | 19 ┊ 0.01% ┊ ⤷ std::panicking::panic_count::is_zero_slow_path::h964c4711602484bf 51 | 17 ┊ 0.01% ┊ ⤷ core::ptr::drop_in_place>::h9106c28cca74d265 52 | 16 ┊ 0.01% ┊ ⤷ core::panicking::panic_cannot_unwind::hf517234e476fc7c7 53 | 16 ┊ 0.01% ┊ ⤷ .Lanon.a9d7ff4935140b8d38885355bf97d04a.323 54 | 10 ┊ 0.00% ┊ ⤷ std::sys::pal::unix::abort_internal::hac8a9a93523d7e74 55 | 5 ┊ 0.00% ┊ ⤷ std::sys::backtrace::__rust_begin_short_backtrace::hfacc9bc09697879e 56 | 4 ┊ 0.00% ┊ ⤷ .Lanon.a9d7ff4935140b8d38885355bf97d04a.237 57 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/elf_dominators3: -------------------------------------------------------------------------------- 1 | Retained Bytes │ Retained % │ Dominator Tree 2 | ────────────────┼────────────┼──────────────────────────────────────────────────────────────────────────────────── 3 | 639 ┊ 0.20% ┊ rust_panic 4 | 535 ┊ 0.17% ┊ ⤷ __rust_start_panic 5 | 287 ┊ 0.09% ┊ ⤷ panic_unwind::imp::panic::exception_cleanup::hd82a5d231fa7c9c4 6 | 93 ┊ 0.03% ┊ ⤷ core::ptr::drop_in_place::hc2382f1a41edffcb 7 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/elf_paths: -------------------------------------------------------------------------------- 1 | Shallow Bytes │ Shallow % │ Retaining Paths 2 | ───────────────┼───────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 3 | 2360 ┊ 0.75% ┊ addr2line::render_file::h8b2b27d4ac1b7166 4 | ┊ ┊ ⬑ addr2line::Lines::parse::hf56d45532c9cdd53 5 | ┊ ┊ ⬑ std::backtrace_rs::symbolize::gimli::Cache::with_global::hda41e5ce2e638dbf 6 | ┊ ┊ ⬑ std::sys::backtrace::_print_fmt::{{closure}}::h70b130b38c57ae5b 7 | ┊ ┊ ⬑ std::backtrace_rs::symbolize::gimli::Context::new::h36f7279e64f729ba 8 | ┊ ┊ ⬑ std::backtrace_rs::symbolize::gimli::Cache::with_global::hda41e5ce2e638dbf 9 | ┊ ┊ ⬑ std::backtrace_rs::symbolize::gimli::elf::::new_debug::h9ec6bbf4be39e1b3 10 | ┊ ┊ ⬑ addr2line::ResUnit::find_function_or_location::{{closure}}::hb0f4e11c2628b129 11 | ┊ ┊ ⬑ std::backtrace_rs::symbolize::gimli::Cache::with_global::hda41e5ce2e638dbf 12 | ┊ ┊ ⬑ addr2line::ResUnit::find_function_or_location::ha089d54f9483837d 13 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/elf_paths2: -------------------------------------------------------------------------------- 1 | Shallow Bytes │ Shallow % │ Retaining Paths 2 | ───────────────┼───────────┼──────────────── 3 | 2136 ┊ 0.68% ┊ main 4 | ┊ ┊ ⬑ _start 5 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/elf_top_25_hello_world_rs: -------------------------------------------------------------------------------- 1 | Shallow Bytes │ Shallow % │ Item 2 | ───────────────┼───────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 3 | 19545 ┊ 6.23% ┊ std::backtrace_rs::symbolize::gimli::Cache::with_global::hda41e5ce2e638dbf 4 | 12227 ┊ 3.90% ┊ std::backtrace_rs::symbolize::gimli::Context::new::h36f7279e64f729ba 5 | 9710 ┊ 3.10% ┊ gimli::read::dwarf::Unit::new::ha14026aa0df75fc3 6 | 9563 ┊ 3.05% ┊ miniz_oxide::inflate::core::decompress::hac20cdac69cf1b03 7 | 6776 ┊ 2.16% ┊ addr2line::ResUnit::find_function_or_location::{{closure}}::hb0f4e11c2628b129 8 | 5918 ┊ 1.89% ┊ addr2line::Lines::parse::hf56d45532c9cdd53 9 | 5016 ┊ 1.60% ┊ addr2line::function::Function::parse_children::ha5a223c2ceda250b 10 | 4715 ┊ 1.50% ┊ gimli::read::unit::parse_attribute::he3623f25de2bc58f 11 | 3792 ┊ 1.21% ┊ gimli::read::rnglists::RngListIter::next::h0a17264f919a4338 12 | 3183 ┊ 1.01% ┊ <&T as core::fmt::Display>::fmt::h98b2a165420fa5eb 13 | 3005 ┊ 0.96% ┊ rustc_demangle::try_demangle::h15f91ce94fd1193f 14 | 2850 ┊ 0.91% ┊ std::backtrace_rs::symbolize::gimli::elf::::new_debug::h9ec6bbf4be39e1b3 15 | 2727 ┊ 0.87% ┊ core::slice::sort::stable::quicksort::quicksort::he669545dd2c45c0a 16 | 2611 ┊ 0.83% ┊ core::slice::sort::stable::quicksort::quicksort::he7b04f966ead366f 17 | 2575 ┊ 0.82% ┊ core::slice::sort::stable::quicksort::quicksort::h3030826ddfc51330 18 | 2525 ┊ 0.80% ┊ gimli::read::line::parse_attribute::hc037578c427b09f8 19 | 2500 ┊ 0.80% ┊ core::slice::sort::stable::quicksort::quicksort::h16560da3e7ccbec2 20 | 2481 ┊ 0.79% ┊ core::slice::sort::stable::quicksort::quicksort::hc3b4b6276e848b30 21 | 2455 ┊ 0.78% ┊ gimli::read::index::UnitIndex::parse::h43e6494ccf64cfc2 22 | 2360 ┊ 0.75% ┊ addr2line::render_file::h8b2b27d4ac1b7166 23 | 2278 ┊ 0.73% ┊ std::panicking::rust_panic_with_hook::h66e909d048c263a9 24 | 2152 ┊ 0.69% ┊ <&str as core::str::pattern::Pattern>::is_contained_in::h8969625a96e5f095 25 | 2136 ┊ 0.68% ┊ main 26 | 1895 ┊ 0.60% ┊ gimli::read::unit::Attribute::value::h1b2c3068fe9de893 27 | 1810 ┊ 0.58% ┊ rustc_demangle::v0::Printer::print_path::h237f306c4cf20d50 28 | 124973 ┊ 39.84% ┊ ... and 415 more. 29 | 241778 ┊ 77.07% ┊ Σ [440 Total Rows] 30 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/garbage: -------------------------------------------------------------------------------- 1 | Bytes │ Size % │ Garbage Item 2 | ───────┼────────┼──────────────────────────────── 3 | 12 ┊ 6.09% ┊ unusedAddThreeNumbers 4 | 9 ┊ 4.57% ┊ unusedAddOne 5 | 7 ┊ 3.55% ┊ type[2]: (i32, i32, i32) -> i32 6 | 6 ┊ 3.05% ┊ unusedChild 7 | 5 ┊ 2.54% ┊ type[1]: (i32) -> i32 8 | 4 ┊ 2.03% ┊ type[0]: () -> i32 9 | 43 ┊ 21.83% ┊ Σ [6 Total Rows] 10 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/garbage_json: -------------------------------------------------------------------------------- 1 | [{"name":"unusedAddThreeNumbers","bytes":12,"size_percent":6.091370558375635},{"name":"unusedAddOne","bytes":9,"size_percent":4.568527918781726},{"name":"type[2]: (i32, i32, i32) -> i32","bytes":7,"size_percent":3.5532994923857872},{"name":"unusedChild","bytes":6,"size_percent":3.0456852791878175},{"name":"type[1]: (i32) -> i32","bytes":5,"size_percent":2.5380710659898478},{"name":"type[0]: () -> i32","bytes":4,"size_percent":2.030456852791878},{"name":"Σ [6 Total Rows]","bytes":43,"size_percent":21.82741116751269}] -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/garbage_top_2: -------------------------------------------------------------------------------- 1 | Bytes │ Size % │ Garbage Item 2 | ───────┼────────┼────────────────────── 3 | 12 ┊ 6.09% ┊ unusedAddThreeNumbers 4 | 9 ┊ 4.57% ┊ unusedAddOne 5 | 22 ┊ 11.17% ┊ ... and 4 more 6 | 43 ┊ 21.83% ┊ Σ [6 Total Rows] 7 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/garbage_top_2_json: -------------------------------------------------------------------------------- 1 | [{"name":"unusedAddThreeNumbers","bytes":12,"size_percent":6.091370558375635},{"name":"unusedAddOne","bytes":9,"size_percent":4.568527918781726},{"name":"... and 4 more","bytes":22,"size_percent":11.16751269035533},{"name":"Σ [6 Total Rows]","bytes":43,"size_percent":21.82741116751269}] -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/garbage_wee_alloc_all: -------------------------------------------------------------------------------- 1 | Bytes │ Size % │ Garbage Item 2 | ───────┼────────┼───────────────────────────────────────── 3 | 0 ┊ 0.00% ┊ Σ [0 Total Rows] 4 | 1084 ┊ 38.48% ┊ 3 potential false-positive data segments 5 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/garbage_wee_alloc_all_json: -------------------------------------------------------------------------------- 1 | [{"name":"Σ [0 Total Rows]","bytes":0,"size_percent":0},{"name":"3 potential false-positive data segments","bytes":1084,"size_percent":38.480653177138805}] -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/garbage_wee_alloc_show_data_segments: -------------------------------------------------------------------------------- 1 | Bytes │ Size % │ Garbage Item 2 | ───────┼────────┼────────────────── 3 | 1034 ┊ 36.71% ┊ data[3] 4 | 25 ┊ 0.89% ┊ data[1] 5 | 25 ┊ 0.89% ┊ data[2] 6 | 1084 ┊ 38.48% ┊ Σ [3 Total Rows] 7 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/garbage_wee_alloc_show_data_segments_json: -------------------------------------------------------------------------------- 1 | [{"name":"data[3]","bytes":1034,"size_percent":36.7057152999645},{"name":"data[1]","bytes":25,"size_percent":0.8874689385871495},{"name":"data[2]","bytes":25,"size_percent":0.8874689385871495},{"name":"Σ [3 Total Rows]","bytes":1084,"size_percent":38.480653177138805}] -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/garbage_wee_alloc_top_10: -------------------------------------------------------------------------------- 1 | Bytes │ Size % │ Garbage Item 2 | ───────┼────────┼───────────────────────────────────────── 3 | 0 ┊ 0.00% ┊ Σ [0 Total Rows] 4 | 1084 ┊ 38.48% ┊ 3 potential false-positive data segments 5 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/garbage_wee_alloc_top_10_json: -------------------------------------------------------------------------------- 1 | [{"name":"Σ [0 Total Rows]","bytes":0,"size_percent":0},{"name":"3 potential false-positive data segments","bytes":1084,"size_percent":38.480653177138805}] -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/garbage_wee_alloc_top_2_show_data_segments: -------------------------------------------------------------------------------- 1 | Bytes │ Size % │ Garbage Item 2 | ───────┼────────┼────────────────── 3 | 1034 ┊ 36.71% ┊ data[3] 4 | 25 ┊ 0.89% ┊ data[1] 5 | 25 ┊ 0.89% ┊ ... and 1 more 6 | 1084 ┊ 38.48% ┊ Σ [3 Total Rows] 7 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/garbage_wee_alloc_top_2_show_data_segments_json: -------------------------------------------------------------------------------- 1 | [{"name":"data[3]","bytes":1034,"size_percent":36.7057152999645},{"name":"data[1]","bytes":25,"size_percent":0.8874689385871495},{"name":"... and 1 more","bytes":25,"size_percent":0.8874689385871495},{"name":"Σ [3 Total Rows]","bytes":1084,"size_percent":38.480653177138805}] -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/issue_16: -------------------------------------------------------------------------------- 1 | Shallow Bytes │ Shallow % │ Retaining Paths 2 | ───────────────┼───────────┼──────────────────────────────────── 3 | 300 ┊ 0.66% ┊ compute_column_spans 4 | ┊ ┊ ⬑ export "compute_column_spans" 5 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/monos_functions: -------------------------------------------------------------------------------- 1 | Apprx. Bloat Bytes │ Apprx. Bloat % │ Bytes │ % │ Monomorphizations 2 | ────────────────────┼────────────────┼───────┼───────┼───────────────────────────────────────────────── 3 | 2141 ┊ 3.68% ┊ 3249 ┊ 5.58% ┊ alloc::slice::merge_sort 4 | ┊ ┊ 1108 ┊ 1.90% ┊ alloc::slice::merge_sort::hb3d195f9800bdad6 5 | ┊ ┊ 1108 ┊ 1.90% ┊ alloc::slice::merge_sort::hfcf2318d7dc71d03 6 | ┊ ┊ 1033 ┊ 1.77% ┊ alloc::slice::merge_sort::hcfca67f5c75a52ef 7 | 236 ┊ 0.41% ┊ 357 ┊ 0.61% ┊ alloc::slice::insert_head 8 | ┊ ┊ 121 ┊ 0.21% ┊ alloc::slice::insert_head::h2cdb84a455761146 9 | ┊ ┊ 121 ┊ 0.21% ┊ alloc::slice::insert_head::haf6e08236bab8bde 10 | ┊ ┊ 115 ┊ 0.20% ┊ alloc::slice::insert_head::hed0e79da03eeec8b 11 | 2377 ┊ 4.08% ┊ 3606 ┊ 6.20% ┊ Σ [8 Total Rows] 12 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/monos_json: -------------------------------------------------------------------------------- 1 | [{"generic":"alloc::slice::merge_sort","approximate_monomorphization_bloat_bytes":2141,"approximate_monomorphization_bloat_percent":3.67844134423751,"total_size":3249,"total_size_percent":5.58209057796715,"monomorphizations":[{"name":"alloc::slice::merge_sort::hb3d195f9800bdad6","shallow_size":1108,"shallow_size_percent":1.9036492337296405},{"name":"... and 2 more.","shallow_size":2141,"shallow_size_percent":3.67844134423751}]},{"generic":"<&'a T as core::fmt::Debug>::fmt","approximate_monomorphization_bloat_bytes":1457,"approximate_monomorphization_bloat_percent":2.5032643804549513,"total_size":4223,"total_size_percent":7.255515084873893,"monomorphizations":[{"name":"<&'a T as core::fmt::Debug>::fmt::h1c27955d8de3ff17","shallow_size":2766,"shallow_size_percent":4.75225070441894},{"name":"... and 3 more.","shallow_size":1457,"shallow_size_percent":2.5032643804549513}]},{"generic":"... and 196 more.","approximate_monomorphization_bloat_bytes":3759,"approximate_monomorphization_bloat_percent":6.458319015875198,"total_size":31160,"total_size_percent":53.53583946120541,"monomorphizations":[]},{"generic":"Σ [202 Total Rows]","approximate_monomorphization_bloat_bytes":7357,"approximate_monomorphization_bloat_percent":12.64002474056766,"total_size":38632,"total_size_percent":66.37344512404646,"monomorphizations":[]}] -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/monos_maxes: -------------------------------------------------------------------------------- 1 | Apprx. Bloat Bytes │ Apprx. Bloat % │ Bytes │ % │ Monomorphizations 2 | ────────────────────┼────────────────┼───────┼────────┼──────────────────────────────────────────────────────── 3 | 2141 ┊ 3.68% ┊ 3249 ┊ 5.58% ┊ alloc::slice::merge_sort 4 | ┊ ┊ 1108 ┊ 1.90% ┊ alloc::slice::merge_sort::hb3d195f9800bdad6 5 | ┊ ┊ 2141 ┊ 3.68% ┊ ... and 2 more. 6 | 1457 ┊ 2.50% ┊ 4223 ┊ 7.26% ┊ <&'a T as core::fmt::Debug>::fmt 7 | ┊ ┊ 2766 ┊ 4.75% ┊ <&'a T as core::fmt::Debug>::fmt::h1c27955d8de3ff17 8 | ┊ ┊ 1457 ┊ 2.50% ┊ ... and 3 more. 9 | 3759 ┊ 6.46% ┊ 31160 ┊ 53.54% ┊ ... and 196 more. 10 | 7357 ┊ 12.64% ┊ 38632 ┊ 66.37% ┊ Σ [202 Total Rows] 11 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/monos_only_generics: -------------------------------------------------------------------------------- 1 | Apprx. Bloat Bytes │ Apprx. Bloat % │ Bytes │ % │ Monomorphizations 2 | ────────────────────┼────────────────┼───────┼────────┼───────────────────────────────────────────────────────────────────────────── 3 | 2141 ┊ 3.68% ┊ 3249 ┊ 5.58% ┊ alloc::slice::merge_sort 4 | 1457 ┊ 2.50% ┊ 4223 ┊ 7.26% ┊ <&'a T as core::fmt::Debug>::fmt 5 | 1204 ┊ 2.07% ┊ 1382 ┊ 2.37% ┊ core::result::unwrap_failed 6 | 658 ┊ 1.13% ┊ 843 ┊ 1.45% ┊ >::double 7 | 574 ┊ 0.99% ┊ 897 ┊ 1.54% ┊ std::thread::local::os::destroy_value 8 | 236 ┊ 0.41% ┊ 357 ┊ 0.61% ┊ alloc::slice::insert_head 9 | 236 ┊ 0.41% ┊ 354 ┊ 0.61% ┊ as core::fmt::Write>::write_fmt 10 | 210 ┊ 0.36% ┊ 290 ┊ 0.50% ┊ >::push 11 | 137 ┊ 0.24% ┊ 207 ┊ 0.36% ┊ as core::slice::SliceIndex<[T]>>::index_mut 12 | 115 ┊ 0.20% ┊ 230 ┊ 0.40% ┊ core::fmt::Write::write_fmt 13 | 389 ┊ 0.67% ┊ 26600 ┊ 45.70% ┊ ... and 81 more. 14 | 7357 ┊ 12.64% ┊ 38632 ┊ 66.37% ┊ Σ [91 Total Rows] 15 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/monos_regex: -------------------------------------------------------------------------------- 1 | Apprx. Bloat Bytes │ Apprx. Bloat % │ Bytes │ % │ Monomorphizations 2 | ────────────────────┼────────────────┼───────┼───────┼───────────────────────────────────────────────────────────────────────────────────── 3 | 2141 ┊ 3.68% ┊ 3249 ┊ 5.58% ┊ alloc::slice::merge_sort 4 | ┊ ┊ 1108 ┊ 1.90% ┊ alloc::slice::merge_sort::hb3d195f9800bdad6 5 | ┊ ┊ 1108 ┊ 1.90% ┊ alloc::slice::merge_sort::hfcf2318d7dc71d03 6 | ┊ ┊ 1033 ┊ 1.77% ┊ alloc::slice::merge_sort::hcfca67f5c75a52ef 7 | 658 ┊ 1.13% ┊ 843 ┊ 1.45% ┊ >::double 8 | ┊ ┊ 185 ┊ 0.32% ┊ >::double::h28f86621ee2a10aa 9 | ┊ ┊ 185 ┊ 0.32% ┊ >::double::h956450b93bdc9e1e 10 | ┊ ┊ 185 ┊ 0.32% ┊ >::double::hcb2fb5861b96a3b0 11 | ┊ ┊ 176 ┊ 0.30% ┊ >::double::ha715b4e5cc3c60ae 12 | ┊ ┊ 112 ┊ 0.19% ┊ >::double::h77ff8547127c5db2 13 | 236 ┊ 0.41% ┊ 357 ┊ 0.61% ┊ alloc::slice::insert_head 14 | ┊ ┊ 121 ┊ 0.21% ┊ alloc::slice::insert_head::h2cdb84a455761146 15 | ┊ ┊ 121 ┊ 0.21% ┊ alloc::slice::insert_head::haf6e08236bab8bde 16 | ┊ ┊ 115 ┊ 0.20% ┊ alloc::slice::insert_head::hed0e79da03eeec8b 17 | 210 ┊ 0.36% ┊ 290 ┊ 0.50% ┊ >::push 18 | ┊ ┊ 80 ┊ 0.14% ┊ >::push::h98b02eda22d1ca25 19 | ┊ ┊ 71 ┊ 0.12% ┊ >::push::h5729b9e7651ef67b 20 | ┊ ┊ 71 ┊ 0.12% ┊ >::push::hc927b4bedb35b00d 21 | ┊ ┊ 68 ┊ 0.12% ┊ >::push::h9415ef699ccc65d8 22 | 40 ┊ 0.07% ┊ 60 ┊ 0.10% ┊ as core::ops::drop::Drop>::drop 23 | ┊ ┊ 20 ┊ 0.03% ┊ as core::ops::drop::Drop>::drop::h420ff33e8bc0de30 24 | ┊ ┊ 20 ┊ 0.03% ┊ as core::ops::drop::Drop>::drop::hab66cea5bda1ed02 25 | ┊ ┊ 20 ┊ 0.03% ┊ as core::ops::drop::Drop>::drop::hbe243f4c44295f3d 26 | 0 ┊ 0.00% ┊ 871 ┊ 1.50% ┊ ... and 16 more. 27 | 3285 ┊ 5.64% ┊ 5670 ┊ 9.74% ┊ Σ [39 Total Rows] 28 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/monos_wasm_csv: -------------------------------------------------------------------------------- 1 | Generic,ApproximateMonomorphizationBloatBytes,ApproximateMonomorphizationBloatPercent,TotalSize,TotalSizePercent,Monomorphizations 2 | alloc::slice::merge_sort,2141,3.67844134423751,3249,5.58209057796715,"alloc::slice::merge_sort::hb3d195f9800bdad6, alloc::slice::merge_sort::hfcf2318d7dc71d03, alloc::slice::merge_sort::hcfca67f5c75a52ef" 3 | <&'a T as core::fmt::Debug>::fmt,1457,2.5032643804549513,4223,7.255515084873893,"<&'a T as core::fmt::Debug>::fmt::h1c27955d8de3ff17, <&'a T as core::fmt::Debug>::fmt::hea6a77c4dcddb7ac, <&'a T as core::fmt::Debug>::fmt::hfbacf6f5c9f53bb2, <&'a T as core::fmt::Debug>::fmt::h199e8e1c5752e6f1" 4 | core::result::unwrap_failed,1204,2.0685863514535083,1382,2.37440725723318,"core::result::unwrap_failed::h137aa4f433aba1a9, core::result::unwrap_failed::h4cc73eb9bf19ce32, core::result::unwrap_failed::h9bd27c3a9ad7c001, core::result::unwrap_failed::h9a7678774db14d67, core::result::unwrap_failed::ha3e58cfc7f422ab4, core::result::unwrap_failed::ha7651fcaac40f701, core::result::unwrap_failed::hcb258ce32bda3d85, core::result::unwrap_failed::hcfddf900474e698a" 5 | ">::double",658,1.1305064943990104,843,1.448354065012714,">::double::h28f86621ee2a10aa, >::double::h956450b93bdc9e1e, >::double::hcb2fb5861b96a3b0, >::double::ha715b4e5cc3c60ae, >::double::h77ff8547127c5db2" 6 | std::thread::local::os::destroy_value,574,0.986186516390626,897,1.5411311937323897,"std::thread::local::os::destroy_value::hca8124786bee4a79, std::thread::local::os::destroy_value::h094cf4f2a025ba2b, std::thread::local::os::destroy_value::h453d41f6c315da32" 7 | alloc::slice::insert_head,236,0.40547041440450826,357,0.6133599065356333,"alloc::slice::insert_head::h2cdb84a455761146, alloc::slice::insert_head::haf6e08236bab8bde, alloc::slice::insert_head::hed0e79da03eeec8b" 8 | " as core::fmt::Write>::write_fmt",236,0.40547041440450826,354,0.6082056216067624," as core::fmt::Write>::write_fmt::h1b74a5fafe15c8eb, as core::fmt::Write>::write_fmt::h24034d1c07bfae93, as core::fmt::Write>::write_fmt::h5ebed3e159974658" 9 | >::push,210,0.36079994502096074,290,0.4982475431241839,">::push::h98b02eda22d1ca25, >::push::h5729b9e7651ef67b, >::push::hc927b4bedb35b00d, >::push::h9415ef699ccc65d8" 10 | as core::slice::SliceIndex<[T]>>::index_mut,137,0.23537901175176962,207,0.35564566009208987," as core::slice::SliceIndex<[T]>>::index_mut::hba42cce6d0c0099b, as core::slice::SliceIndex<[T]>>::index_mut::hbf8fcfe76c1f6657, as core::slice::SliceIndex<[T]>>::index_mut::h1c053f01b6f95d93" 11 | core::fmt::Write::write_fmt,115,0.19758092227338328,230,0.39516184454676656,"core::fmt::Write::write_fmt::ha5ae3249cacba520, core::fmt::Write::write_fmt::hef4632e1398f5ac8" 12 | ... and 187 more.,389,0.6683389457769225,26600,45.7013263693217, 13 | Σ [235 Total Rows],7357,12.64002474056766,38632,66.37344512404646, 14 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/output_to_file: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustwasm/twiggy/f6230a8a0f0d262695a66016bbd92a4f1aef46ee/twiggy/tests/all/expectations/output_to_file -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/paths_error_test_no_max_paths: -------------------------------------------------------------------------------- 1 | Shallow Bytes │ Shallow % │ Retaining Paths 2 | ───────────────┼───────────┼──────────────────────────────────────────────────────────────────────────────────── 3 | 340 ┊ 0.75% ┊ std::io::error::Error::new::h8c006d5367bc92ed 4 | ┊ ┊ ⬑ std::io::impls::::write::h5d7e5ba58acd05fd 5 | ┊ ┊ ⬑ elem[0] 6 | ┊ ┊ ⬑ table[0] 7 | ┊ ┊ ⬑ std::panicking::LOCAL_STDERR::__getit::h7827294b3348067a 8 | ┊ ┊ ⬑ elem[0] 9 | ┊ ┊ ⬑ table[0] 10 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/paths_error_test_no_max_paths_csv: -------------------------------------------------------------------------------- 1 | Name,ShallowSize,ShallowSizePercent,Path 2 | std::io::error::Error::new::h8c006d5367bc92ed,340,0.7516802263883976,std::io::impls::::write::h5d7e5ba58acd05fd -> std::panicking::LOCAL_STDERR::__getit::h7827294b3348067a -> std::io::error::Error::new::h8c006d5367bc92ed 3 | std::io::impls::::write::h5d7e5ba58acd05fd,312,0.6897771489211179,elem[0] -> std::io::impls::::write::h5d7e5ba58acd05fd 4 | elem[0],59,0.13043862752033958,table[0] -> elem[0] 5 | table[0],4,0.008843296781039971, 6 | std::panicking::LOCAL_STDERR::__getit::h7827294b3348067a,17,0.03758401131941988,elem[0] -> std::panicking::LOCAL_STDERR::__getit::h7827294b3348067a 7 | elem[0],59,0.13043862752033958,table[0] -> elem[0] 8 | table[0],4,0.008843296781039971, 9 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/paths_error_test_no_max_paths_json: -------------------------------------------------------------------------------- 1 | [{"name":"std::io::error::Error::new::h8c006d5367bc92ed","shallow_size":340,"shallow_size_percent":0.7516802263883976,"callers":[{"name":"std::io::impls::::write::h5d7e5ba58acd05fd","shallow_size":312,"shallow_size_percent":0.6897771489211179,"callers":[{"name":"elem[0]","shallow_size":59,"shallow_size_percent":0.13043862752033958,"callers":[{"name":"table[0]","shallow_size":4,"shallow_size_percent":0.008843296781039971,"callers":[]}]}]},{"name":"std::panicking::LOCAL_STDERR::__getit::h7827294b3348067a","shallow_size":17,"shallow_size_percent":0.03758401131941988,"callers":[{"name":"elem[0]","shallow_size":59,"shallow_size_percent":0.13043862752033958,"callers":[{"name":"table[0]","shallow_size":4,"shallow_size_percent":0.008843296781039971,"callers":[]}]}]}]}] -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/paths_error_test_one_path: -------------------------------------------------------------------------------- 1 | Shallow Bytes │ Shallow % │ Retaining Paths 2 | ───────────────┼───────────┼──────────────────────────────────────────────────────────────────────────────────── 3 | 340 ┊ 0.75% ┊ std::io::error::Error::new::h8c006d5367bc92ed 4 | ┊ ┊ ⬑ std::io::impls::::write::h5d7e5ba58acd05fd 5 | ┊ ┊ ⬑ elem[0] 6 | ┊ ┊ ⬑ table[0] 7 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/paths_error_test_one_path_csv: -------------------------------------------------------------------------------- 1 | Name,ShallowSize,ShallowSizePercent,Path 2 | std::io::error::Error::new::h8c006d5367bc92ed,340,0.7516802263883976,std::io::impls::::write::h5d7e5ba58acd05fd -> std::panicking::LOCAL_STDERR::__getit::h7827294b3348067a -> std::io::error::Error::new::h8c006d5367bc92ed 3 | std::io::impls::::write::h5d7e5ba58acd05fd,312,0.6897771489211179,elem[0] -> std::io::impls::::write::h5d7e5ba58acd05fd 4 | elem[0],59,0.13043862752033958,table[0] -> elem[0] 5 | table[0],4,0.008843296781039971, 6 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/paths_error_test_one_path_json: -------------------------------------------------------------------------------- 1 | [{"name":"std::io::error::Error::new::h8c006d5367bc92ed","shallow_size":340,"shallow_size_percent":0.7516802263883976,"callers":[{"name":"std::io::impls::::write::h5d7e5ba58acd05fd","shallow_size":312,"shallow_size_percent":0.6897771489211179,"callers":[{"name":"elem[0]","shallow_size":59,"shallow_size_percent":0.13043862752033958,"callers":[{"name":"table[0]","shallow_size":4,"shallow_size_percent":0.008843296781039971,"callers":[]}]}]}]}] -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/paths_test_called_once: -------------------------------------------------------------------------------- 1 | Shallow Bytes │ Shallow % │ Retaining Paths 2 | ───────────────┼───────────┼──────────────────────── 3 | 6 ┊ 4.17% ┊ calledOnce 4 | ┊ ┊ ⬑ woof 5 | ┊ ┊ ⬑ export "woof" 6 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/paths_test_called_once_csv: -------------------------------------------------------------------------------- 1 | Name,ShallowSize,ShallowSizePercent,Path 2 | calledOnce,6,4.166666666666666,woof -> calledOnce 3 | woof,9,6.25,"export ""woof"" -> woof" 4 | "export ""woof""",7,4.861111111111112, 5 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/paths_test_called_once_json: -------------------------------------------------------------------------------- 1 | [{"name":"calledOnce","shallow_size":6,"shallow_size_percent":4.166666666666666,"callers":[{"name":"woof","shallow_size":9,"shallow_size_percent":6.25,"callers":[{"name":"export \"woof\"","shallow_size":7,"shallow_size_percent":4.861111111111112,"callers":[]}]}]}] -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/paths_test_called_twice: -------------------------------------------------------------------------------- 1 | Shallow Bytes │ Shallow % │ Retaining Paths 2 | ───────────────┼───────────┼──────────────────────────── 3 | 6 ┊ 4.17% ┊ calledTwice 4 | ┊ ┊ ⬑ bark 5 | ┊ ┊ ⬑ export "bark" 6 | ┊ ┊ ⬑ awoo 7 | ┊ ┊ ⬑ export "awoo" 8 | ┊ ┊ ⬑ woof 9 | ┊ ┊ ⬑ export "woof" 10 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/paths_test_called_twice_csv: -------------------------------------------------------------------------------- 1 | Name,ShallowSize,ShallowSizePercent,Path 2 | calledTwice,6,4.166666666666666,bark -> woof -> calledTwice 3 | bark,6,4.166666666666666,"export ""bark"" -> awoo -> bark" 4 | "export ""bark""",7,4.861111111111112, 5 | awoo,6,4.166666666666666,"export ""awoo"" -> awoo" 6 | "export ""awoo""",7,4.861111111111112, 7 | woof,9,6.25,"export ""woof"" -> woof" 8 | "export ""woof""",7,4.861111111111112, 9 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/paths_test_called_twice_json: -------------------------------------------------------------------------------- 1 | [{"name":"calledTwice","shallow_size":6,"shallow_size_percent":4.166666666666666,"callers":[{"name":"bark","shallow_size":6,"shallow_size_percent":4.166666666666666,"callers":[{"name":"export \"bark\"","shallow_size":7,"shallow_size_percent":4.861111111111112,"callers":[]},{"name":"awoo","shallow_size":6,"shallow_size_percent":4.166666666666666,"callers":[{"name":"export \"awoo\"","shallow_size":7,"shallow_size_percent":4.861111111111112,"callers":[]}]}]},{"name":"woof","shallow_size":9,"shallow_size_percent":6.25,"callers":[{"name":"export \"woof\"","shallow_size":7,"shallow_size_percent":4.861111111111112,"callers":[]}]}]}] -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/paths_test_default_output: -------------------------------------------------------------------------------- 1 | Shallow Bytes │ Shallow % │ Retaining Paths 2 | ───────────────┼───────────┼──────────────────────────────── 3 | 46 ┊ 31.94% ┊ "function names" subsection 4 | 13 ┊ 9.03% ┊ "local names" subsection 5 | 9 ┊ 6.25% ┊ woof 6 | ┊ ┊ ⬑ export "woof" 7 | 8 ┊ 5.56% ┊ wasm magic bytes 8 | 7 ┊ 4.86% ┊ export "awoo" 9 | 7 ┊ 4.86% ┊ export "bark" 10 | 7 ┊ 4.86% ┊ export "woof" 11 | 7 ┊ 4.86% ┊ custom section 'name' headers 12 | 6 ┊ 4.17% ┊ calledOnce 13 | ┊ ┊ ⬑ woof 14 | ┊ ┊ ⬑ export "woof" 15 | 6 ┊ 4.17% ┊ calledTwice 16 | ┊ ┊ ⬑ bark 17 | ┊ ┊ ⬑ export "bark" 18 | ┊ ┊ ⬑ awoo 19 | ┊ ┊ ⬑ export "awoo" 20 | ┊ ┊ ⬑ woof 21 | ┊ ┊ ⬑ export "woof" 22 | 6 ┊ 4.17% ┊ bark 23 | ┊ ┊ ⬑ export "bark" 24 | ┊ ┊ ⬑ awoo 25 | ┊ ┊ ⬑ export "awoo" 26 | 6 ┊ 4.17% ┊ awoo 27 | ┊ ┊ ⬑ export "awoo" 28 | 6 ┊ 4.17% ┊ code section headers 29 | 4 ┊ 2.78% ┊ type[0]: () -> i32 30 | ┊ ┊ ⬑ calledOnce 31 | ┊ ┊ ⬑ woof 32 | ┊ ┊ ⬑ export "woof" 33 | ┊ ┊ ⬑ calledTwice 34 | ┊ ┊ ⬑ bark 35 | ┊ ┊ ⬑ export "bark" 36 | ┊ ┊ ⬑ awoo 37 | ┊ ┊ ⬑ export "awoo" 38 | ┊ ┊ ⬑ woof 39 | ┊ ┊ ⬑ export "woof" 40 | ┊ ┊ ⬑ bark 41 | ┊ ┊ ⬑ export "bark" 42 | ┊ ┊ ⬑ awoo 43 | ┊ ┊ ⬑ export "awoo" 44 | ┊ ┊ ⬑ woof 45 | ┊ ┊ ⬑ export "woof" 46 | ┊ ┊ ⬑ awoo 47 | ┊ ┊ ⬑ export "awoo" 48 | 3 ┊ 2.08% ┊ type section headers 49 | 3 ┊ 2.08% ┊ export section headers 50 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/paths_test_default_output_csv: -------------------------------------------------------------------------------- 1 | Name,ShallowSize,ShallowSizePercent,Path 2 | """function names"" subsection",46,31.944444444444443, 3 | """local names"" subsection",13,9.027777777777777, 4 | woof,9,6.25,"export ""woof"" -> woof" 5 | "export ""woof""",7,4.861111111111112, 6 | wasm magic bytes,8,5.555555555555555, 7 | "export ""awoo""",7,4.861111111111112, 8 | "export ""bark""",7,4.861111111111112, 9 | "export ""woof""",7,4.861111111111112, 10 | custom section 'name' headers,7,4.861111111111112, 11 | calledOnce,6,4.166666666666666,woof -> calledOnce 12 | woof,9,6.25,"export ""woof"" -> woof" 13 | "export ""woof""",7,4.861111111111112, 14 | calledTwice,6,4.166666666666666,bark -> woof -> calledTwice 15 | bark,6,4.166666666666666,"export ""bark"" -> awoo -> bark" 16 | "export ""bark""",7,4.861111111111112, 17 | awoo,6,4.166666666666666,"export ""awoo"" -> awoo" 18 | "export ""awoo""",7,4.861111111111112, 19 | woof,9,6.25,"export ""woof"" -> woof" 20 | "export ""woof""",7,4.861111111111112, 21 | bark,6,4.166666666666666,"export ""bark"" -> awoo -> bark" 22 | "export ""bark""",7,4.861111111111112, 23 | awoo,6,4.166666666666666,"export ""awoo"" -> awoo" 24 | "export ""awoo""",7,4.861111111111112, 25 | awoo,6,4.166666666666666,"export ""awoo"" -> awoo" 26 | "export ""awoo""",7,4.861111111111112, 27 | code section headers,6,4.166666666666666, 28 | type[0]: () -> i32,4,2.7777777777777777,calledOnce -> calledTwice -> bark -> woof -> awoo -> type[0]: () -> i32 29 | calledOnce,6,4.166666666666666,woof -> calledOnce 30 | woof,9,6.25,"export ""woof"" -> woof" 31 | "export ""woof""",7,4.861111111111112, 32 | calledTwice,6,4.166666666666666,bark -> woof -> calledTwice 33 | bark,6,4.166666666666666,"export ""bark"" -> awoo -> bark" 34 | "export ""bark""",7,4.861111111111112, 35 | awoo,6,4.166666666666666,"export ""awoo"" -> awoo" 36 | "export ""awoo""",7,4.861111111111112, 37 | woof,9,6.25,"export ""woof"" -> woof" 38 | "export ""woof""",7,4.861111111111112, 39 | bark,6,4.166666666666666,"export ""bark"" -> awoo -> bark" 40 | "export ""bark""",7,4.861111111111112, 41 | awoo,6,4.166666666666666,"export ""awoo"" -> awoo" 42 | "export ""awoo""",7,4.861111111111112, 43 | woof,9,6.25,"export ""woof"" -> woof" 44 | "export ""woof""",7,4.861111111111112, 45 | awoo,6,4.166666666666666,"export ""awoo"" -> awoo" 46 | "export ""awoo""",7,4.861111111111112, 47 | type section headers,3,2.083333333333333, 48 | export section headers,3,2.083333333333333, 49 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/paths_test_default_output_desc: -------------------------------------------------------------------------------- 1 | Shallow Bytes │ Shallow % │ Retaining Paths 2 | ───────────────┼───────────┼───────────────────────────────────── 3 | 46 ┊ 31.94% ┊ "function names" subsection 4 | 13 ┊ 9.03% ┊ "local names" subsection 5 | 8 ┊ 5.56% ┊ wasm magic bytes 6 | 7 ┊ 4.86% ┊ export "awoo" 7 | ┊ ┊ ↳ awoo 8 | ┊ ┊ ↳ type[0]: () -> i32 9 | ┊ ┊ ↳ bark 10 | ┊ ┊ ↳ type[0]: () -> i32 11 | ┊ ┊ ↳ calledTwice 12 | ┊ ┊ ↳ type[0]: () -> i32 13 | 7 ┊ 4.86% ┊ export "bark" 14 | ┊ ┊ ↳ bark 15 | ┊ ┊ ↳ type[0]: () -> i32 16 | ┊ ┊ ↳ calledTwice 17 | ┊ ┊ ↳ type[0]: () -> i32 18 | 7 ┊ 4.86% ┊ export "woof" 19 | ┊ ┊ ↳ woof 20 | ┊ ┊ ↳ type[0]: () -> i32 21 | ┊ ┊ ↳ calledOnce 22 | ┊ ┊ ↳ type[0]: () -> i32 23 | ┊ ┊ ↳ calledTwice 24 | ┊ ┊ ↳ type[0]: () -> i32 25 | 7 ┊ 4.86% ┊ custom section 'name' headers 26 | 6 ┊ 4.17% ┊ code section headers 27 | 3 ┊ 2.08% ┊ type section headers 28 | 3 ┊ 2.08% ┊ export section headers 29 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/paths_test_default_output_desc_with_depth: -------------------------------------------------------------------------------- 1 | Shallow Bytes │ Shallow % │ Retaining Paths 2 | ───────────────┼───────────┼────────────────────────────── 3 | 46 ┊ 31.94% ┊ "function names" subsection 4 | 13 ┊ 9.03% ┊ "local names" subsection 5 | 8 ┊ 5.56% ┊ wasm magic bytes 6 | 7 ┊ 4.86% ┊ export "awoo" 7 | ┊ ┊ ↳ awoo 8 | ┊ ┊ ↳ type[0]: () -> i32 9 | ┊ ┊ ↳ bark 10 | 7 ┊ 4.86% ┊ export "bark" 11 | ┊ ┊ ↳ bark 12 | ┊ ┊ ↳ type[0]: () -> i32 13 | ┊ ┊ ↳ calledTwice 14 | 7 ┊ 4.86% ┊ export "woof" 15 | ┊ ┊ ↳ woof 16 | ┊ ┊ ↳ type[0]: () -> i32 17 | ┊ ┊ ↳ calledOnce 18 | ┊ ┊ ↳ calledTwice 19 | 7 ┊ 4.86% ┊ custom section 'name' headers 20 | 6 ┊ 4.17% ┊ code section headers 21 | 3 ┊ 2.08% ┊ type section headers 22 | 3 ┊ 2.08% ┊ export section headers 23 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/paths_test_default_output_json: -------------------------------------------------------------------------------- 1 | [{"name":"\"function names\" subsection","shallow_size":46,"shallow_size_percent":31.944444444444443,"callers":[]},{"name":"\"local names\" subsection","shallow_size":13,"shallow_size_percent":9.027777777777777,"callers":[]},{"name":"woof","shallow_size":9,"shallow_size_percent":6.25,"callers":[{"name":"export \"woof\"","shallow_size":7,"shallow_size_percent":4.861111111111112,"callers":[]}]},{"name":"wasm magic bytes","shallow_size":8,"shallow_size_percent":5.555555555555555,"callers":[]},{"name":"export \"awoo\"","shallow_size":7,"shallow_size_percent":4.861111111111112,"callers":[]},{"name":"export \"bark\"","shallow_size":7,"shallow_size_percent":4.861111111111112,"callers":[]},{"name":"export \"woof\"","shallow_size":7,"shallow_size_percent":4.861111111111112,"callers":[]},{"name":"custom section 'name' headers","shallow_size":7,"shallow_size_percent":4.861111111111112,"callers":[]},{"name":"calledOnce","shallow_size":6,"shallow_size_percent":4.166666666666666,"callers":[{"name":"woof","shallow_size":9,"shallow_size_percent":6.25,"callers":[{"name":"export \"woof\"","shallow_size":7,"shallow_size_percent":4.861111111111112,"callers":[]}]}]},{"name":"calledTwice","shallow_size":6,"shallow_size_percent":4.166666666666666,"callers":[{"name":"bark","shallow_size":6,"shallow_size_percent":4.166666666666666,"callers":[{"name":"export \"bark\"","shallow_size":7,"shallow_size_percent":4.861111111111112,"callers":[]},{"name":"awoo","shallow_size":6,"shallow_size_percent":4.166666666666666,"callers":[{"name":"export \"awoo\"","shallow_size":7,"shallow_size_percent":4.861111111111112,"callers":[]}]}]},{"name":"woof","shallow_size":9,"shallow_size_percent":6.25,"callers":[{"name":"export \"woof\"","shallow_size":7,"shallow_size_percent":4.861111111111112,"callers":[]}]}]},{"name":"bark","shallow_size":6,"shallow_size_percent":4.166666666666666,"callers":[{"name":"export \"bark\"","shallow_size":7,"shallow_size_percent":4.861111111111112,"callers":[]},{"name":"awoo","shallow_size":6,"shallow_size_percent":4.166666666666666,"callers":[{"name":"export \"awoo\"","shallow_size":7,"shallow_size_percent":4.861111111111112,"callers":[]}]}]},{"name":"awoo","shallow_size":6,"shallow_size_percent":4.166666666666666,"callers":[{"name":"export \"awoo\"","shallow_size":7,"shallow_size_percent":4.861111111111112,"callers":[]}]},{"name":"code section headers","shallow_size":6,"shallow_size_percent":4.166666666666666,"callers":[]},{"name":"type[0]: () -> i32","shallow_size":4,"shallow_size_percent":2.7777777777777777,"callers":[{"name":"calledOnce","shallow_size":6,"shallow_size_percent":4.166666666666666,"callers":[{"name":"woof","shallow_size":9,"shallow_size_percent":6.25,"callers":[{"name":"export \"woof\"","shallow_size":7,"shallow_size_percent":4.861111111111112,"callers":[]}]}]},{"name":"calledTwice","shallow_size":6,"shallow_size_percent":4.166666666666666,"callers":[{"name":"bark","shallow_size":6,"shallow_size_percent":4.166666666666666,"callers":[{"name":"export \"bark\"","shallow_size":7,"shallow_size_percent":4.861111111111112,"callers":[]},{"name":"awoo","shallow_size":6,"shallow_size_percent":4.166666666666666,"callers":[{"name":"export \"awoo\"","shallow_size":7,"shallow_size_percent":4.861111111111112,"callers":[]}]}]},{"name":"woof","shallow_size":9,"shallow_size_percent":6.25,"callers":[{"name":"export \"woof\"","shallow_size":7,"shallow_size_percent":4.861111111111112,"callers":[]}]}]},{"name":"bark","shallow_size":6,"shallow_size_percent":4.166666666666666,"callers":[{"name":"export \"bark\"","shallow_size":7,"shallow_size_percent":4.861111111111112,"callers":[]},{"name":"awoo","shallow_size":6,"shallow_size_percent":4.166666666666666,"callers":[{"name":"export \"awoo\"","shallow_size":7,"shallow_size_percent":4.861111111111112,"callers":[]}]}]},{"name":"woof","shallow_size":9,"shallow_size_percent":6.25,"callers":[{"name":"export \"woof\"","shallow_size":7,"shallow_size_percent":4.861111111111112,"callers":[]}]},{"name":"awoo","shallow_size":6,"shallow_size_percent":4.166666666666666,"callers":[{"name":"export \"awoo\"","shallow_size":7,"shallow_size_percent":4.861111111111112,"callers":[]}]}]},{"name":"type section headers","shallow_size":3,"shallow_size_percent":2.083333333333333,"callers":[]},{"name":"export section headers","shallow_size":3,"shallow_size_percent":2.083333333333333,"callers":[]}] -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/paths_test_regex_called_any: -------------------------------------------------------------------------------- 1 | Shallow Bytes │ Shallow % │ Retaining Paths 2 | ───────────────┼───────────┼──────────────────────────── 3 | 6 ┊ 4.17% ┊ calledOnce 4 | ┊ ┊ ⬑ woof 5 | ┊ ┊ ⬑ export "woof" 6 | 6 ┊ 4.17% ┊ calledTwice 7 | ┊ ┊ ⬑ bark 8 | ┊ ┊ ⬑ export "bark" 9 | ┊ ┊ ⬑ awoo 10 | ┊ ┊ ⬑ export "awoo" 11 | ┊ ┊ ⬑ woof 12 | ┊ ┊ ⬑ export "woof" 13 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/paths_test_regex_exports: -------------------------------------------------------------------------------- 1 | Shallow Bytes │ Shallow % │ Retaining Paths 2 | ───────────────┼───────────┼──────────────── 3 | 7 ┊ 4.86% ┊ export "awoo" 4 | 7 ┊ 4.86% ┊ export "bark" 5 | 7 ┊ 4.86% ┊ export "woof" 6 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/paths_test_regex_exports_desc: -------------------------------------------------------------------------------- 1 | Shallow Bytes │ Shallow % │ Retaining Paths 2 | ───────────────┼───────────┼───────────────────────────────────── 3 | 7 ┊ 4.86% ┊ export "awoo" 4 | ┊ ┊ ↳ awoo 5 | ┊ ┊ ↳ type[0]: () -> i32 6 | ┊ ┊ ↳ bark 7 | ┊ ┊ ↳ type[0]: () -> i32 8 | ┊ ┊ ↳ calledTwice 9 | ┊ ┊ ↳ type[0]: () -> i32 10 | 7 ┊ 4.86% ┊ export "bark" 11 | ┊ ┊ ↳ bark 12 | ┊ ┊ ↳ type[0]: () -> i32 13 | ┊ ┊ ↳ calledTwice 14 | ┊ ┊ ↳ type[0]: () -> i32 15 | 7 ┊ 4.86% ┊ export "woof" 16 | ┊ ┊ ↳ woof 17 | ┊ ┊ ↳ type[0]: () -> i32 18 | ┊ ┊ ↳ calledOnce 19 | ┊ ┊ ↳ type[0]: () -> i32 20 | ┊ ┊ ↳ calledTwice 21 | ┊ ┊ ↳ type[0]: () -> i32 22 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/paths_wee_alloc: -------------------------------------------------------------------------------- 1 | Shallow Bytes │ Shallow % │ Retaining Paths 2 | ───────────────┼───────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 3 | 226 ┊ 8.02% ┊ wee_alloc::alloc_first_fit::h9a72de3af77ef93f 4 | ┊ ┊ ⬑ wee_alloc::alloc_with_refill::hb32c1bbce9ebda8e 5 | ┊ ┊ ⬑ as wee_alloc::AllocPolicy>::new_cell_for_free_list::h3987e3054b8224e6 6 | ┊ ┊ ⬑ elem[0] 7 | ┊ ┊ ⬑ table[0] 8 | ┊ ┊ ⬑ hello 9 | ┊ ┊ ⬑ export "hello" 10 | 165 ┊ 5.86% ┊ hello 11 | ┊ ┊ ⬑ export "hello" 12 | 45 ┊ 1.60% ┊ goodbye 13 | ┊ ┊ ⬑ export "goodbye" 14 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/paths_wee_alloc_csv: -------------------------------------------------------------------------------- 1 | Name,ShallowSize,ShallowSizePercent,Path 2 | wee_alloc::alloc_first_fit::h9a72de3af77ef93f,226,8.022719204827832,wee_alloc::alloc_with_refill::hb32c1bbce9ebda8e -> wee_alloc::alloc_first_fit::h9a72de3af77ef93f 3 | wee_alloc::alloc_with_refill::hb32c1bbce9ebda8e,153,5.431309904153355, as wee_alloc::AllocPolicy>::new_cell_for_free_list::h3987e3054b8224e6 -> hello -> wee_alloc::alloc_with_refill::hb32c1bbce9ebda8e 4 | as wee_alloc::AllocPolicy>::new_cell_for_free_list::h3987e3054b8224e6,137,4.863329783457579,elem[0] -> as wee_alloc::AllocPolicy>::new_cell_for_free_list::h3987e3054b8224e6 5 | elem[0],12,0.42598509052183176,table[0] -> elem[0] 6 | table[0],4,0.1419950301739439, 7 | hello,165,5.857294994675186,"export ""hello"" -> hello" 8 | "export ""hello""",8,0.2839900603478878, 9 | hello,165,5.857294994675186,"export ""hello"" -> hello" 10 | "export ""hello""",8,0.2839900603478878, 11 | goodbye,45,1.5974440894568689,"export ""goodbye"" -> goodbye" 12 | "export ""goodbye""",10,0.3549875754348598, 13 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/paths_wee_alloc_json: -------------------------------------------------------------------------------- 1 | [{"name":"wee_alloc::alloc_first_fit::h9a72de3af77ef93f","shallow_size":226,"shallow_size_percent":8.022719204827832,"callers":[{"name":"wee_alloc::alloc_with_refill::hb32c1bbce9ebda8e","shallow_size":153,"shallow_size_percent":5.431309904153355,"callers":[{"name":" as wee_alloc::AllocPolicy>::new_cell_for_free_list::h3987e3054b8224e6","shallow_size":137,"shallow_size_percent":4.863329783457579,"callers":[{"name":"elem[0]","shallow_size":12,"shallow_size_percent":0.42598509052183176,"callers":[]}]},{"name":"hello","shallow_size":165,"shallow_size_percent":5.857294994675186,"callers":[{"name":"export \"hello\"","shallow_size":8,"shallow_size_percent":0.2839900603478878,"callers":[]}]}]}]},{"name":"hello","shallow_size":165,"shallow_size_percent":5.857294994675186,"callers":[{"name":"export \"hello\"","shallow_size":8,"shallow_size_percent":0.2839900603478878,"callers":[]}]},{"name":"goodbye","shallow_size":45,"shallow_size_percent":1.5974440894568689,"callers":[{"name":"export \"goodbye\"","shallow_size":10,"shallow_size_percent":0.3549875754348598,"callers":[]}]}] -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/paths_wee_alloc_with_depth_and_paths: -------------------------------------------------------------------------------- 1 | Shallow Bytes │ Shallow % │ Retaining Paths 2 | ───────────────┼───────────┼────────────────────────────────────────────────────── 3 | 226 ┊ 8.02% ┊ wee_alloc::alloc_first_fit::h9a72de3af77ef93f 4 | ┊ ┊ ⬑ wee_alloc::alloc_with_refill::hb32c1bbce9ebda8e 5 | 165 ┊ 5.86% ┊ hello 6 | ┊ ┊ ⬑ export "hello" 7 | 45 ┊ 1.60% ┊ goodbye 8 | ┊ ┊ ⬑ export "goodbye" 9 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/paths_wee_alloc_with_depth_and_paths_csv: -------------------------------------------------------------------------------- 1 | Name,ShallowSize,ShallowSizePercent,Path 2 | wee_alloc::alloc_first_fit::h9a72de3af77ef93f,226,8.022719204827832,wee_alloc::alloc_with_refill::hb32c1bbce9ebda8e -> wee_alloc::alloc_first_fit::h9a72de3af77ef93f 3 | wee_alloc::alloc_with_refill::hb32c1bbce9ebda8e,153,5.431309904153355, as wee_alloc::AllocPolicy>::new_cell_for_free_list::h3987e3054b8224e6 -> hello -> wee_alloc::alloc_with_refill::hb32c1bbce9ebda8e 4 | hello,165,5.857294994675186,"export ""hello"" -> hello" 5 | "export ""hello""",8,0.2839900603478878, 6 | goodbye,45,1.5974440894568689,"export ""goodbye"" -> goodbye" 7 | "export ""goodbye""",10,0.3549875754348598, 8 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/paths_wee_alloc_with_depth_and_paths_json: -------------------------------------------------------------------------------- 1 | [{"name":"wee_alloc::alloc_first_fit::h9a72de3af77ef93f","shallow_size":226,"shallow_size_percent":8.022719204827832,"callers":[{"name":"wee_alloc::alloc_with_refill::hb32c1bbce9ebda8e","shallow_size":153,"shallow_size_percent":5.431309904153355,"callers":[]}]},{"name":"hello","shallow_size":165,"shallow_size_percent":5.857294994675186,"callers":[{"name":"export \"hello\"","shallow_size":8,"shallow_size_percent":0.2839900603478878,"callers":[]}]},{"name":"goodbye","shallow_size":45,"shallow_size_percent":1.5974440894568689,"callers":[{"name":"export \"goodbye\"","shallow_size":10,"shallow_size_percent":0.3549875754348598,"callers":[]}]}] -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/top_2_csv: -------------------------------------------------------------------------------- 1 | Name,ShallowSize,ShallowSizePercent,RetainedSize,RetainedSizePercent 2 | data[3],1034,36.7057152999645,, 3 | """function names"" subsection",777,27.582534611288605,, 4 | wee_alloc::alloc_first_fit::h9a72de3af77ef93f,226,8.022719204827832,, 5 | hello,165,5.857294994675186,, 6 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/top_2_csv_retained: -------------------------------------------------------------------------------- 1 | Name,ShallowSize,ShallowSizePercent,RetainedSize,RetainedSizePercent 2 | data[3],1034,36.7057152999645,1034,36.7057152999645 3 | """function names"" subsection",777,27.582534611288605,777,27.582534611288605 4 | wee_alloc::alloc_with_refill::hb32c1bbce9ebda8e,153,5.431309904153355,387,13.738019169329075 5 | table[0],4,0.1419950301739439,271,9.620163294284701 6 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/top_2_json: -------------------------------------------------------------------------------- 1 | [{"name":"data[3]","shallow_size":1034,"shallow_size_percent":36.7057152999645},{"name":"\"function names\" subsection","shallow_size":777,"shallow_size_percent":27.582534611288605}] -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/top_2_json_retained: -------------------------------------------------------------------------------- 1 | [{"name":"data[3]","shallow_size":1034,"shallow_size_percent":36.7057152999645,"retained_size":1034,"retained_size_percent":36.7057152999645},{"name":"\"function names\" subsection","shallow_size":777,"shallow_size_percent":27.582534611288605,"retained_size":777,"retained_size_percent":27.582534611288605}] -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/top_mappings: -------------------------------------------------------------------------------- 1 | Shallow Bytes │ Shallow % │ Item 2 | ───────────────┼───────────┼────────────────────────────────────────────────────────────────────────────── 3 | 5285 ┊ 11.68% ┊ core::ptr::drop_in_place::hddeb26218033f78b.1290 4 | 4340 ┊ 9.59% ┊ "function names" subsection 5 | 1976 ┊ 4.37% ┊ memmove 6 | 1753 ┊ 3.88% ┊ >::try_with::h81b6490c42d3c021 7 | 1705 ┊ 3.77% ┊ parse_mappings 8 | 1513 ┊ 3.34% ┊ ::fmt::h17b46907f43c196c 9 | 1278 ┊ 2.83% ┊ core::ptr::drop_in_place::h15636b240d3542f7 10 | 1220 ┊ 2.70% ┊ memset 11 | 1218 ┊ 2.69% ┊ __powidf2 12 | 1143 ┊ 2.53% ┊ __udivsi3 13 | 23801 ┊ 52.62% ┊ ... and 268 more. 14 | 45232 ┊ 100.00% ┊ Σ [278 Total Rows] 15 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/top_memory_module: -------------------------------------------------------------------------------- 1 | Shallow Bytes │ Shallow % │ Item 2 | ───────────────┼───────────┼────────────────────────────── 3 | 8 ┊ 12.70% ┊ wasm magic bytes 4 | 7 ┊ 11.11% ┊ f 5 | 7 ┊ 11.11% ┊ custom section 'name' headers 6 | 6 ┊ 9.52% ┊ code section headers 7 | 6 ┊ 9.52% ┊ "function names" subsection 8 | 6 ┊ 9.52% ┊ "memory names" subsection 9 | 5 ┊ 7.94% ┊ data[0] 10 | 3 ┊ 4.76% ┊ type[0]: () -> nil 11 | 3 ┊ 4.76% ┊ type section headers 12 | 3 ┊ 4.76% ┊ memory[0] 13 | 9 ┊ 14.29% ┊ ... and 3 more. 14 | 63 ┊ 100.00% ┊ Σ [13 Total Rows] 15 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/top_mono: -------------------------------------------------------------------------------- 1 | Shallow Bytes │ Shallow % │ Item 2 | ───────────────┼───────────┼─────────────────────────────────── 3 | 876523 ┊ 15.63% ┊ "function names" subsection 4 | 205501 ┊ 3.66% ┊ elem[0] 5 | 190603 ┊ 3.40% ┊ data[3154] 6 | 177026 ┊ 3.16% ┊ _interp_exec_method_full 7 | 157090 ┊ 2.80% ┊ _generate 8 | 47889 ┊ 0.85% ┊ data[3088] 9 | 39528 ┊ 0.70% ┊ data[2041] 10 | 32685 ┊ 0.58% ┊ _SHA1Transform 11 | 27795 ┊ 0.50% ┊ _major_scan_object_with_evacuation 12 | 26913 ┊ 0.48% ┊ _major_scan_object_no_evacuation 13 | 3828171 ┊ 68.24% ┊ ... and 80684 more. 14 | 5609724 ┊ 100.00% ┊ Σ [80694 Total Rows] 15 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/top_retained_mappings: -------------------------------------------------------------------------------- 1 | Retained Bytes │ Retained % │ Item 2 | ────────────────┼────────────┼───────────────────────────────────────────────────────────────────────────────────────────────── 3 | 14492 ┊ 32.04% ┊ table[0] 4 | 14488 ┊ 32.03% ┊ elem[0] 5 | 5285 ┊ 11.68% ┊ core::ptr::drop_in_place::hddeb26218033f78b.1290 6 | 4340 ┊ 9.59% ┊ "function names" subsection 7 | 3440 ┊ 7.61% ┊ export "parse_mappings" 8 | 3423 ┊ 7.57% ┊ parse_mappings 9 | 3091 ┊ 6.83% ┊ __powidf2 10 | 2722 ┊ 6.02% ┊ __divsf3 11 | 2274 ┊ 5.03% ┊ as core::fmt::Write>::write_char::hc0e93e852c5108e1 12 | 1976 ┊ 4.37% ┊ memmove 13 | ... ┊ ... ┊ ... and 268 more. 14 | ... ┊ ... ┊ Σ [278 Total Rows] 15 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/top_retained_wee_alloc: -------------------------------------------------------------------------------- 1 | Retained Bytes │ Retained % │ Item 2 | ────────────────┼────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 3 | 1034 ┊ 36.71% ┊ data[3] 4 | 777 ┊ 27.58% ┊ "function names" subsection 5 | 387 ┊ 13.74% ┊ wee_alloc::alloc_with_refill::hb32c1bbce9ebda8e 6 | 271 ┊ 9.62% ┊ table[0] 7 | 267 ┊ 9.48% ┊ elem[0] 8 | 226 ┊ 8.02% ┊ wee_alloc::alloc_first_fit::h9a72de3af77ef93f 9 | 177 ┊ 6.28% ┊ export "hello" 10 | 169 ┊ 6.00% ┊ hello 11 | 137 ┊ 4.86% ┊ as wee_alloc::AllocPolicy>::new_cell_for_free_list::h3987e3054b8224e6 12 | 77 ┊ 2.73% ┊ ::new_cell_for_free_list::h8f071b7bce0301ba 13 | ... ┊ ... ┊ ... and 27 more. 14 | ... ┊ ... ┊ Σ [37 Total Rows] 15 | -------------------------------------------------------------------------------- /twiggy/tests/all/expectations/top_wee_alloc: -------------------------------------------------------------------------------- 1 | Shallow Bytes │ Shallow % │ Item 2 | ───────────────┼───────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 3 | 1034 ┊ 36.71% ┊ data[3] 4 | 777 ┊ 27.58% ┊ "function names" subsection 5 | 226 ┊ 8.02% ┊ wee_alloc::alloc_first_fit::h9a72de3af77ef93f 6 | 165 ┊ 5.86% ┊ hello 7 | 153 ┊ 5.43% ┊ wee_alloc::alloc_with_refill::hb32c1bbce9ebda8e 8 | 137 ┊ 4.86% ┊ as wee_alloc::AllocPolicy>::new_cell_for_free_list::h3987e3054b8224e6 9 | 77 ┊ 2.73% ┊ ::new_cell_for_free_list::h8f071b7bce0301ba 10 | 45 ┊ 1.60% ┊ goodbye 11 | 25 ┊ 0.89% ┊ data[1] 12 | 25 ┊ 0.89% ┊ data[2] 13 | 153 ┊ 5.43% ┊ ... and 27 more. 14 | 2817 ┊ 100.00% ┊ Σ [37 Total Rows] 15 | -------------------------------------------------------------------------------- /twiggy/tests/all/fixtures/cpp-monos.cpp: -------------------------------------------------------------------------------- 1 | // https://webassembly.studio/?f=zbgxunzkbf 2 | 3 | #include 4 | #include 5 | 6 | #define WASM_EXPORT __attribute__((visibility("default"))) 7 | 8 | using CodeFn = uint32_t (*)(); 9 | 10 | extern "C" { 11 | void hello(CodeFn f); 12 | } 13 | 14 | template 15 | __attribute__((noinline)) 16 | void generic() { hello(T::code); } 17 | 18 | struct Zero { 19 | static uint32_t code() { return 0; } 20 | }; 21 | 22 | struct One { 23 | static uint32_t code() { return 1; } 24 | }; 25 | 26 | struct Two { 27 | static uint32_t code() { return 2; } 28 | }; 29 | 30 | WASM_EXPORT void trigger_generic_monos() { 31 | generic(); 32 | generic(); 33 | generic(); 34 | } 35 | 36 | WASM_EXPORT 37 | int main() { return 42; } 38 | -------------------------------------------------------------------------------- /twiggy/tests/all/fixtures/cpp-monos.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustwasm/twiggy/f6230a8a0f0d262695a66016bbd92a4f1aef46ee/twiggy/tests/all/fixtures/cpp-monos.wasm -------------------------------------------------------------------------------- /twiggy/tests/all/fixtures/garbage.wasm: -------------------------------------------------------------------------------- 1 | asm````add 2 | !A  j 3 | j j  j oname8 unusedChild unusedAddOneunusedAddThreeNumbersadd.valfirstsecondthirdlhsrhs -------------------------------------------------------------------------------- /twiggy/tests/all/fixtures/garbage.wat: -------------------------------------------------------------------------------- 1 | (module 2 | ;; ------------------------------------------------------------------------- 3 | ;; This is a WebAssembly text file that can be compiled in a wasm module to 4 | ;; test the `twiggy garbage` command. This file contains exported functions, 5 | ;; as well as unreachable functions of different sizes. 6 | ;; ------------------------------------------------------------------------- 7 | ;; NOTE: The test cases expect that this module is compiled with debug 8 | ;; names written to the binary file, which affects the size percentages. 9 | ;; Compile this file using the following command: 10 | ;; 11 | ;; wat2wasm --debug-names garbage.wat -o garbage.wasm 12 | ;; ------------------------------------------------------------------------- 13 | 14 | ;; This unused function is called by 'unusedAddOne'. Push 1 onto the stack. 15 | (func $unusedChild (result i32) 16 | i32.const 1) 17 | 18 | ;; This unused function will call `unusedChild`, and return `val + 1`. 19 | (func $unusedAddOne (param $val i32) (result i32) 20 | get_local $val 21 | call $unusedChild 22 | i32.add) 23 | 24 | ;; This unused function adds three numbers, and returns the result. 25 | (func $unusedAddThreeNumbers 26 | (param $first i32) (param $second i32) (param $third i32) (result i32) 27 | get_local $first 28 | get_local $second 29 | i32.add 30 | get_local $third 31 | i32.add 32 | ) 33 | 34 | ;; This function exists to test that reachable items are not shown. 35 | (func $add (param $lhs i32) (param $rhs i32) (result i32) 36 | get_local $lhs 37 | get_local $rhs 38 | i32.add 39 | ) 40 | 41 | ;; Export only the `add` function. 42 | (export "add" (func $add)) 43 | ) -------------------------------------------------------------------------------- /twiggy/tests/all/fixtures/hello_elf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustwasm/twiggy/f6230a8a0f0d262695a66016bbd92a4f1aef46ee/twiggy/tests/all/fixtures/hello_elf -------------------------------------------------------------------------------- /twiggy/tests/all/fixtures/hello_world.rs: -------------------------------------------------------------------------------- 1 | //! Rebuild `hello_elf` with: 2 | //! 3 | //! ``` 4 | //! rustc --target x86_64-unknown-linux-gnu hello_world.rs -o hello_elf -C lto=fat -C opt-level=z -Clink-arg=-Wl,--emit-relocs 5 | //! ``` 6 | //! 7 | //! Rebuild `hello_mach` with: 8 | //! 9 | //! ``` 10 | //! rustc +nightly --target x86_64-apple-darwin hello_world.rs -o hello_mach.o -C lto=fat -C opt-level=z 11 | //! ``` 12 | //! NOTE: The above is not working for me on Ubuntu. This causes an error when `ld` is invoked. 13 | 14 | fn main() { 15 | println!("Hello, world!"); 16 | } 17 | -------------------------------------------------------------------------------- /twiggy/tests/all/fixtures/mappings.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustwasm/twiggy/f6230a8a0f0d262695a66016bbd92a4f1aef46ee/twiggy/tests/all/fixtures/mappings.wasm -------------------------------------------------------------------------------- /twiggy/tests/all/fixtures/memory.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustwasm/twiggy/f6230a8a0f0d262695a66016bbd92a4f1aef46ee/twiggy/tests/all/fixtures/memory.wasm -------------------------------------------------------------------------------- /twiggy/tests/all/fixtures/memory.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (memory $0 1 1) 3 | (data (i32.const 0) "") 4 | (func $f (data.drop 0)) 5 | ) 6 | -------------------------------------------------------------------------------- /twiggy/tests/all/fixtures/mono.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustwasm/twiggy/f6230a8a0f0d262695a66016bbd92a4f1aef46ee/twiggy/tests/all/fixtures/mono.wasm -------------------------------------------------------------------------------- /twiggy/tests/all/fixtures/monos.rs: -------------------------------------------------------------------------------- 1 | //! Rebuild `monos.wasm` with: 2 | //! 3 | //! ``` 4 | //! rustc +nightly -g --target wasm32-unknown-unknown monos.rs -o monos.wasm -C lto=fat -C opt-level=z 5 | //! ``` 6 | 7 | #![cfg(target_arch = "wasm32")] 8 | #![crate_type = "cdylib"] 9 | 10 | use std::slice; 11 | 12 | extern "C" { 13 | fn hello(f: extern "C" fn() -> u32); 14 | } 15 | 16 | trait Code { 17 | extern "C" fn code() -> u32; 18 | } 19 | 20 | #[inline(never)] 21 | fn generic() { 22 | unsafe { 23 | hello(C::code); 24 | } 25 | } 26 | 27 | struct Zero; 28 | impl Code for Zero { 29 | extern "C" fn code() -> u32 { 30 | 0 31 | } 32 | } 33 | 34 | struct One; 35 | impl Code for One { 36 | extern "C" fn code() -> u32 { 37 | 1 38 | } 39 | } 40 | 41 | struct Two; 42 | impl Code for Two { 43 | extern "C" fn code() -> u32 { 44 | 2 45 | } 46 | } 47 | 48 | #[no_mangle] 49 | pub extern "C" fn trigger_generic_monos() { 50 | generic::(); 51 | generic::(); 52 | generic::(); 53 | } 54 | 55 | #[no_mangle] 56 | pub extern "C" fn sort_u32s(ptr: *mut u32, len: usize) { 57 | unsafe { 58 | let slice = slice::from_raw_parts_mut(ptr, len); 59 | slice.sort(); 60 | } 61 | } 62 | 63 | #[no_mangle] 64 | pub extern "C" fn sort_u8s(ptr: *mut u8, len: usize) { 65 | unsafe { 66 | let slice = slice::from_raw_parts_mut(ptr, len); 67 | slice.sort(); 68 | } 69 | } 70 | 71 | #[no_mangle] 72 | pub extern "C" fn sort_i32s(ptr: *mut i32, len: usize) { 73 | unsafe { 74 | let slice = slice::from_raw_parts_mut(ptr, len); 75 | slice.sort(); 76 | } 77 | } 78 | 79 | #[no_mangle] 80 | pub extern "C" fn push_and_sort_u32s(ptr: *mut u32, cap: usize, len: usize) { 81 | unsafe { 82 | let mut vec = Vec::from_raw_parts(ptr, len, cap); 83 | vec.push(0); 84 | vec.push(1); 85 | vec.push(2); 86 | vec.sort(); 87 | } 88 | } 89 | 90 | #[no_mangle] 91 | pub extern "C" fn push_and_sort_u8s(ptr: *mut u8, cap: usize, len: usize) { 92 | unsafe { 93 | let mut vec = Vec::from_raw_parts(ptr, len, cap); 94 | vec.push(0); 95 | vec.push(1); 96 | vec.push(2); 97 | vec.sort(); 98 | } 99 | } 100 | 101 | #[no_mangle] 102 | pub extern "C" fn push_and_sort_i32s(ptr: *mut i32, cap: usize, len: usize) { 103 | unsafe { 104 | let mut vec = Vec::from_raw_parts(ptr, len, cap); 105 | vec.push(0); 106 | vec.push(1); 107 | vec.push(2); 108 | vec.sort(); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /twiggy/tests/all/fixtures/monos.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustwasm/twiggy/f6230a8a0f0d262695a66016bbd92a4f1aef46ee/twiggy/tests/all/fixtures/monos.wasm -------------------------------------------------------------------------------- /twiggy/tests/all/fixtures/paths_test.wasm: -------------------------------------------------------------------------------- 1 | asm`awoobarkwoof 2 | A A  j  @name, 3 | calledOnce calledTwicebarkwoofawoo  -------------------------------------------------------------------------------- /twiggy/tests/all/fixtures/paths_test.wat: -------------------------------------------------------------------------------- 1 | (module 2 | ;; ------------------------------------------------------------------------ 3 | ;; This is a WebAssembly text file that can be compiled in a wasm module to 4 | ;; test the `twiggy paths` command. This intends to provide a non-trivial 5 | ;; structure of call paths for testing purposes. 6 | ;; 7 | ;; The call path is shown in the ascii diagram below with exported 8 | ;; functions enclosed in braces, and unexported functions in quotes. 9 | ;; 10 | ;; [awoo] 11 | ;; | 12 | ;; v 13 | ;; [woof] [bark] 14 | ;; | | | 15 | ;; | -------- | 16 | ;; | | | 17 | ;; v v v 18 | ;; 'calledOnce' 'calledTwice' 19 | ;; ------------------------------------------------------------------------ 20 | ;; NOTE: The test cases expect that this module is compiled with debug 21 | ;; names written to the binary file, which affects the size percentages. 22 | ;; Compile this file using the following command: 23 | ;; 24 | ;; wat2wasm --debug-names paths_test.wat -o paths_test.wasm 25 | ;; ------------------------------------------------------------------------- 26 | 27 | 28 | ;; This function is called once, by 'woof'. 29 | (func $calledOnce (result i32) 30 | i32.const 1) 31 | 32 | ;; This function is called twice, by 'bark' and 'woof'. 33 | (func $calledTwice (result i32) 34 | i32.const 2) 35 | 36 | (func $bark (result i32) 37 | call $calledTwice) 38 | 39 | (func $woof (result i32) 40 | call $calledOnce 41 | call $calledTwice 42 | i32.add) 43 | 44 | (func $awoo (result i32) 45 | call $bark) 46 | 47 | (export "awoo" (func $awoo)) 48 | (export "bark" (func $bark)) 49 | (export "woof" (func $woof)) 50 | ) 51 | -------------------------------------------------------------------------------- /twiggy/tests/all/fixtures/wee_alloc.2.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustwasm/twiggy/f6230a8a0f0d262695a66016bbd92a4f1aef46ee/twiggy/tests/all/fixtures/wee_alloc.2.wasm -------------------------------------------------------------------------------- /twiggy/tests/all/fixtures/wee_alloc.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustwasm/twiggy/f6230a8a0f0d262695a66016bbd92a4f1aef46ee/twiggy/tests/all/fixtures/wee_alloc.wasm -------------------------------------------------------------------------------- /twiggy/tests/all/garbage_tests.rs: -------------------------------------------------------------------------------- 1 | test!(garbage, "garbage", "./fixtures/garbage.wasm"); 2 | 3 | test!( 4 | garbage_top_2, 5 | "garbage", 6 | "./fixtures/garbage.wasm", 7 | "-n", 8 | "2" 9 | ); 10 | 11 | test!( 12 | garbage_json, 13 | "garbage", 14 | "./fixtures/garbage.wasm", 15 | "-f", 16 | "json" 17 | ); 18 | 19 | test!( 20 | garbage_top_2_json, 21 | "garbage", 22 | "./fixtures/garbage.wasm", 23 | "-f", 24 | "json", 25 | "-n", 26 | "2" 27 | ); 28 | 29 | test!( 30 | garbage_wee_alloc_top_10, 31 | "garbage", 32 | "./fixtures/wee_alloc.wasm" 33 | ); 34 | 35 | test!( 36 | garbage_wee_alloc_all, 37 | "garbage", 38 | "./fixtures/wee_alloc.wasm", 39 | "-a" 40 | ); 41 | 42 | test!( 43 | garbage_wee_alloc_top_10_json, 44 | "garbage", 45 | "./fixtures/wee_alloc.wasm", 46 | "-f", 47 | "json" 48 | ); 49 | 50 | test!( 51 | garbage_wee_alloc_all_json, 52 | "garbage", 53 | "./fixtures/wee_alloc.wasm", 54 | "-f", 55 | "json", 56 | "-a" 57 | ); 58 | 59 | test!( 60 | garbage_wee_alloc_show_data_segments, 61 | "garbage", 62 | "./fixtures/wee_alloc.wasm", 63 | "--show-data-segments" 64 | ); 65 | 66 | test!( 67 | garbage_wee_alloc_top_2_show_data_segments, 68 | "garbage", 69 | "./fixtures/wee_alloc.wasm", 70 | "--show-data-segments", 71 | "-n", 72 | "2" 73 | ); 74 | 75 | test!( 76 | garbage_wee_alloc_show_data_segments_json, 77 | "garbage", 78 | "./fixtures/wee_alloc.wasm", 79 | "--show-data-segments", 80 | "-f", 81 | "json" 82 | ); 83 | 84 | test!( 85 | garbage_wee_alloc_top_2_show_data_segments_json, 86 | "garbage", 87 | "./fixtures/wee_alloc.wasm", 88 | "--show-data-segments", 89 | "-f", 90 | "json", 91 | "-n", 92 | "2" 93 | ); 94 | -------------------------------------------------------------------------------- /twiggy/tests/all/main.rs: -------------------------------------------------------------------------------- 1 | use std::fs; 2 | use std::io::{self, Read}; 3 | use std::path::Path; 4 | 5 | fn slurp>(path: P) -> io::Result> { 6 | let mut f = fs::File::open(path)?; 7 | let mut buf = vec![]; 8 | f.read_to_end(&mut buf)?; 9 | Ok(buf) 10 | } 11 | 12 | macro_rules! test { 13 | ( $name:ident $( , $args:expr )* ) => { 14 | #[test] 15 | fn $name() { 16 | use std::fs; 17 | use std::process::Command; 18 | use colored::Colorize; 19 | use diff; 20 | use crate::slurp; 21 | 22 | let output = Command::new("cargo") 23 | .arg("run") 24 | .arg("--") 25 | $( 26 | .arg($args) 27 | )* 28 | .current_dir(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/all/")) 29 | .output() 30 | .unwrap(); 31 | 32 | assert!( 33 | output.status.success(), 34 | "should have run `twiggy` OK\n\n\ 35 | ============================== stdout ==============================\n\n\ 36 | {}\n\n\ 37 | ============================== stderr ==============================\n\n\ 38 | {}\n\n", 39 | String::from_utf8_lossy(&output.stdout), 40 | String::from_utf8_lossy(&output.stderr), 41 | ); 42 | 43 | let expected_path = concat!( 44 | env!("CARGO_MANIFEST_DIR"), 45 | "/tests/all/expectations/", 46 | stringify!($name) 47 | ); 48 | 49 | // Ignore errors. The diffing will provide a better diagnostic report. 50 | let expected = slurp(expected_path).unwrap_or(vec![]); 51 | let expected = String::from_utf8_lossy(&expected); 52 | let expected_lines = expected.lines().collect::>(); 53 | 54 | let actual = String::from_utf8_lossy(&output.stdout); 55 | 56 | if ::std::env::var("TWIGGY_UPDATE_TEST_EXPECTATIONS").is_ok() { 57 | fs::write(expected_path, actual.as_ref()).unwrap(); 58 | return; 59 | } 60 | 61 | let actual_lines = actual.lines().collect::>(); 62 | 63 | if actual_lines != expected_lines { 64 | let mut cmd = "twiggy".to_string(); 65 | $( 66 | cmd.push(' '); 67 | cmd.push_str($args); 68 | )* 69 | println!( 70 | "\n{} {}\n", 71 | format!("`{}`", cmd).red(), 72 | "did not have the expected output!".red() 73 | ); 74 | println!("--- {}", expected_path); 75 | println!( 76 | "{} {}\n", 77 | "+++ actually generated by".red(), 78 | format!("`{}`", cmd).red() 79 | ); 80 | for diff in diff::slice(&expected_lines, &actual_lines) { 81 | match diff { 82 | diff::Result::Left(l) => { 83 | println!("{}", format!("-{}", l).red()) 84 | } 85 | diff::Result::Both(l, _) => println!(" {}", l), 86 | diff::Result::Right(r) => { 87 | println!("{}", format!("+{}", r).red()) 88 | } 89 | } 90 | } 91 | println!(); 92 | panic!(); 93 | } 94 | } 95 | } 96 | } 97 | 98 | mod diff_tests; 99 | mod dominators_tests; 100 | mod elf_format_tests; 101 | mod garbage_tests; 102 | mod monos_tests; 103 | mod paths_tests; 104 | mod top_tests; 105 | -------------------------------------------------------------------------------- /twiggy/tests/all/monos_tests.rs: -------------------------------------------------------------------------------- 1 | test!(cpp_monos, "monos", "./fixtures/cpp-monos.wasm"); 2 | 3 | test!(monos, "monos", "./fixtures/monos.wasm"); 4 | 5 | test!( 6 | monos_maxes, 7 | "monos", 8 | "./fixtures/monos.wasm", 9 | "-m", 10 | "2", 11 | "-n", 12 | "1" 13 | ); 14 | 15 | test!(monos_only_generics, "monos", "./fixtures/monos.wasm", "-g"); 16 | 17 | test!( 18 | monos_wasm_csv, 19 | "monos", 20 | "./fixtures/monos.wasm", 21 | "-f", 22 | "csv" 23 | ); 24 | 25 | test!(monos_all, "monos", "./fixtures/monos.wasm", "-a"); 26 | 27 | test!( 28 | monos_only_all_generics, 29 | "monos", 30 | "./fixtures/monos.wasm", 31 | "-g", 32 | "-a" 33 | ); 34 | 35 | test!( 36 | monos_all_generics, 37 | "monos", 38 | "./fixtures/monos.wasm", 39 | "--all-generics" 40 | ); 41 | 42 | test!( 43 | monos_all_monos, 44 | "monos", 45 | "./fixtures/monos.wasm", 46 | "--all-monos" 47 | ); 48 | 49 | test!( 50 | monos_json, 51 | "monos", 52 | "./fixtures/monos.wasm", 53 | "-m", 54 | "2", 55 | "-n", 56 | "1", 57 | "-f", 58 | "json" 59 | ); 60 | 61 | test!( 62 | monos_regex, 63 | "monos", 64 | "./fixtures/monos.wasm", 65 | "--regex", 66 | "-m", 67 | "5", 68 | "^ anyhow::Result<()> { 21 | let mut items = parser::read_and_parse(opts.input(), opts.parse_mode())?; 22 | 23 | let data = match opts { 24 | opt::Options::Top(ref top) => analyze::top(&mut items, top)?, 25 | opt::Options::Dominators(ref doms) => analyze::dominators(&mut items, doms)?, 26 | opt::Options::Paths(ref paths) => analyze::paths(&mut items, paths)?, 27 | opt::Options::Monos(ref monos) => analyze::monos(&mut items, monos)?, 28 | opt::Options::Garbage(ref garbo) => analyze::garbage(&items, garbo)?, 29 | opt::Options::Diff(ref diff) => { 30 | let mut new_items = parser::read_and_parse(diff.new_input(), opts.parse_mode())?; 31 | analyze::diff(&mut items, &mut new_items, diff)? 32 | } 33 | }; 34 | 35 | let mut dest = opts.output_destination().open()?; 36 | 37 | data.emit(&items, &mut *dest, opts.output_format()) 38 | } 39 | -------------------------------------------------------------------------------- /wasm-api/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "twiggy-wasm-api" 3 | version = "0.7.0" 4 | authors = ["Nick Fitzgerald "] 5 | license = "Apache-2.0/MIT" 6 | edition = "2018" 7 | 8 | [lib] 9 | crate-type = ["cdylib"] 10 | path = "./wasm-api.rs" 11 | 12 | [dependencies.twiggy-ir] 13 | version = "=0.7.0" 14 | path = "../ir" 15 | 16 | [dependencies.twiggy-analyze] 17 | default-features = false 18 | version = "=0.7.0" 19 | path = "../analyze" 20 | features = ["emit_json"] 21 | 22 | [dependencies.twiggy-opt] 23 | default-features = false 24 | features = ["wasm"] 25 | version = "=0.7.0" 26 | path = "../opt" 27 | 28 | [dependencies.twiggy-parser] 29 | default-features = false 30 | version = "=0.7.0" 31 | path = "../parser" 32 | 33 | [dependencies.twiggy-traits] 34 | version = "=0.7.0" 35 | path = "../traits" 36 | 37 | [dependencies.wasm-bindgen] 38 | version = "0.2.100" 39 | 40 | [features] 41 | default = ["emit_json"] 42 | emit_json = ["twiggy-traits/emit_json"] 43 | -------------------------------------------------------------------------------- /wasm-api/wasm-api.rs: -------------------------------------------------------------------------------- 1 | #![cfg(target_arch = "wasm32")] 2 | #![cfg(feature = "emit_json")] 3 | 4 | use twiggy_analyze as analyze; 5 | use twiggy_ir as ir; 6 | use twiggy_opt as opt; 7 | use twiggy_parser as parser; 8 | use wasm_bindgen::prelude::*; 9 | 10 | #[wasm_bindgen] 11 | pub struct Items { 12 | items: ir::Items, 13 | } 14 | 15 | #[wasm_bindgen] 16 | impl Items { 17 | pub fn parse(data: &[u8]) -> Items { 18 | let items = parser::parse(data).unwrap(); 19 | Items { items } 20 | } 21 | 22 | pub fn top(&mut self, options: &opt::Top) -> String { 23 | let top = analyze::top(&mut self.items, options).unwrap(); 24 | let mut buf = Vec::new(); 25 | top.emit_json(&self.items, &mut buf).unwrap(); 26 | String::from_utf8(buf).unwrap() 27 | } 28 | 29 | pub fn dominators(&mut self, options: &opt::Dominators) -> String { 30 | let dominators = analyze::dominators(&mut self.items, options).unwrap(); 31 | let mut buf = Vec::new(); 32 | dominators.emit_json(&self.items, &mut buf).unwrap(); 33 | String::from_utf8(buf).unwrap() 34 | } 35 | 36 | pub fn paths(&mut self, options: &opt::Paths) -> String { 37 | let paths = analyze::paths(&mut self.items, options).unwrap(); 38 | let mut buf = Vec::new(); 39 | paths.emit_json(&self.items, &mut buf).unwrap(); 40 | String::from_utf8(buf).unwrap() 41 | } 42 | 43 | pub fn monos(&mut self, options: &opt::Monos) -> String { 44 | let monos = analyze::monos(&mut self.items, options).unwrap(); 45 | let mut buf = Vec::new(); 46 | monos.emit_json(&self.items, &mut buf).unwrap(); 47 | String::from_utf8(buf).unwrap() 48 | } 49 | 50 | pub fn diff(&mut self, new_items: &mut Items, options: &opt::Diff) -> String { 51 | let diff = analyze::diff(&mut self.items, &mut new_items.items, options).unwrap(); 52 | let mut buf = Vec::new(); 53 | diff.emit_json(&self.items, &mut buf).unwrap(); 54 | String::from_utf8(buf).unwrap() 55 | } 56 | } 57 | --------------------------------------------------------------------------------