├── .codecov.yml ├── .gitattributes ├── .github ├── DOCS.md ├── codecov.yml ├── dependabot.yml └── workflows │ ├── check.yml │ ├── scheduled.yml │ └── test.yml ├── .gitignore ├── .gitmodules ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── benches ├── collapse.rs └── flamegraph.rs ├── compare.sh ├── src ├── bin │ ├── collapse-dtrace.rs │ ├── collapse-ghcprof.rs │ ├── collapse-guess.rs │ ├── collapse-perf.rs │ ├── collapse-recursive.rs │ ├── collapse-sample.rs │ ├── collapse-vsprof.rs │ ├── collapse-vtune.rs │ ├── collapse-xctrace.rs │ ├── diff-folded.rs │ └── flamegraph.rs ├── collapse │ ├── common.rs │ ├── dtrace.rs │ ├── ghcprof.rs │ ├── guess.rs │ ├── matcher.rs │ ├── mod.rs │ ├── perf.rs │ ├── recursive.rs │ ├── sample.rs │ ├── vsprof.rs │ ├── vtune.rs │ └── xctrace.rs ├── differential │ └── mod.rs ├── flamegraph │ ├── attrs.rs │ ├── color │ │ ├── mod.rs │ │ ├── palette_map.rs │ │ └── palettes.rs │ ├── flamegraph.css │ ├── flamegraph.js │ ├── merge.rs │ ├── mod.rs │ ├── rand.rs │ └── svg.rs └── lib.rs └── tests ├── collapse-dtrace.rs ├── collapse-ghcprof.rs ├── collapse-guess.rs ├── collapse-perf.rs ├── collapse-recursive.rs ├── collapse-sample.rs ├── collapse-vsprof.rs ├── collapse-vtune.rs ├── collapse-xctrace.rs ├── common ├── collapse.rs └── mod.rs ├── data ├── collapse-dtrace │ ├── flamegraph-bug.txt │ ├── hex-addresses.txt │ ├── java.txt │ ├── only-header-lines.txt │ ├── results │ │ ├── dtrace-example-offsets.orig │ │ ├── dtrace-example-offsets.txt │ │ ├── dtrace-example.txt │ │ ├── flamegraph-bug.txt │ │ ├── hex-addresses.txt │ │ ├── java.txt │ │ ├── rust-names.txt │ │ ├── scope_with_no_argument_list.txt │ │ └── stack-ustack.txt │ ├── rust-names.txt │ ├── scope_with_no_argument_list.txt │ └── stack-ustack.txt ├── collapse-ghcprof │ ├── percent.prof │ ├── results │ │ ├── percent.txt │ │ ├── ticks.txt │ │ ├── ticks_bytes.txt │ │ ├── ticks_ticks.txt │ │ ├── utf8.txt │ │ ├── utf8_bytes.txt │ │ └── utf8_ticks.txt │ ├── ticks.prof │ └── utf8.prof ├── collapse-guess │ ├── invalid-perf-with-empty-line-after-event-line.txt │ └── unknown-format.txt ├── collapse-perf │ ├── cpp-stacks-std-function.txt │ ├── empty-line.txt │ ├── go-stacks.txt │ ├── java-inline.txt │ ├── no-events.txt │ ├── results │ │ ├── cpp-stacks-std-function-collapsed.txt │ │ ├── example-perf-stacks-collapsed.txt │ │ ├── go-stacks-collapsed.txt │ │ ├── java-inline-collapsed.txt │ │ ├── no-events-collapsed.txt │ │ ├── perf-rust-Yamakaky-dcpu-collapsed-addrs.txt │ │ ├── perf-rust-Yamakaky-dcpu-collapsed-all.txt │ │ ├── perf-rust-Yamakaky-dcpu-collapsed-jit.txt │ │ ├── perf-rust-Yamakaky-dcpu-collapsed-kernel.txt │ │ ├── perf-rust-Yamakaky-dcpu-collapsed-pid.txt │ │ ├── perf-rust-Yamakaky-dcpu-collapsed-tid.txt │ │ ├── single-event-collapsed.txt │ │ ├── single-line-stacks-collapsed.txt │ │ ├── sourcepawn-jitdump-collapsed-jit.txt │ │ └── versioned-vmlinux-collapsed-kernel.txt │ ├── single-event.txt │ ├── single-line-stacks.txt │ ├── sourcepawn-jitdump.txt │ ├── versioned-vmlinux.txt │ └── weird-stack-line.txt ├── collapse-recursive │ ├── basic.txt │ └── results │ │ └── basic-collapsed.txt ├── collapse-sample │ ├── bad-stack-line.txt │ ├── end-before-call-graph-end.txt │ ├── end-before-call-graph-start.txt │ ├── invalid-samples-field.txt │ ├── large.txt.gz │ ├── no-four-spaces.txt │ ├── odd-indentation.txt │ ├── results │ │ ├── sample-default.txt │ │ └── sample-no-modules.txt │ ├── sample.txt │ ├── skipped-indentation.txt │ └── stack-line-only-indent-chars.txt ├── collapse-vsprof │ ├── CallTreeSummary.csv │ ├── empty-file.csv │ ├── incorrect-header.csv │ ├── invalid-depth.csv │ ├── invalid-function-name.csv │ ├── invalid-number-of-calls.csv │ ├── missing-function-name.csv │ └── results │ │ ├── sample-default.txt │ │ └── vsprof-default.txt ├── collapse-vtune │ ├── bad-stack-line.csv │ ├── end-before-header.csv │ ├── invalid-time-field.csv │ ├── results │ │ ├── vtune-default.txt │ │ └── vtune-no-modules.txt │ ├── skipped-indentation.csv │ └── vtune.csv ├── collapse-xctrace │ ├── basic.xml │ ├── results │ │ ├── basic.folded │ │ └── simple_frame_without_binary_info.folded │ └── simple_frame_without_binary_info.xml ├── diff-folded │ ├── after.txt │ ├── bad_before.txt │ ├── before.txt │ ├── before_fractionals.txt │ └── results │ │ ├── default.txt │ │ ├── fractionals.txt │ │ ├── normalize.txt │ │ └── strip_hex.txt └── flamegraph │ ├── austin │ ├── flame.svg │ └── flames.txt │ ├── bad-lines │ └── bad-lines.txt │ ├── base │ ├── flames.txt │ ├── multi-base.svg │ └── single-base.svg │ ├── colors │ ├── async-profiler-collapsed-part.txt │ ├── async-profiler-java.svg │ ├── deterministic.svg │ ├── java.svg │ └── js.svg │ ├── differential │ ├── diff-negated.svg │ ├── diff.svg │ └── perf-cycles-instructions-01-collapsed-all-diff.txt │ ├── empty │ ├── empty.svg │ └── empty.txt │ ├── example-perf-stacks │ ├── example-perf-stacks.svg │ └── palette.map │ ├── factor │ └── factor-2.5.svg │ ├── flamechart │ ├── flame.svg │ └── flames.txt │ ├── fractional-samples │ ├── fractional-reversed.svg │ ├── fractional.txt │ ├── no-fractionals.txt │ ├── tricky-stack.txt │ ├── with-space-reversed.svg │ ├── with-space.txt │ └── zero-fractionals.txt │ ├── grey-frames │ ├── grey-frames.svg │ └── grey-frames.txt │ ├── inverted │ └── inverted.svg │ ├── multiple-inputs │ ├── perf-vertx-stacks-01-collapsed-all-unsorted-1.txt │ └── perf-vertx-stacks-01-collapsed-all-unsorted-2.txt │ ├── nameattr │ ├── nameattr.svg │ ├── nameattr.txt │ ├── nameattr_duplicate_attributes.svg │ ├── nameattr_duplicate_attributes.txt │ ├── nameattr_empty_attribute.txt │ ├── nameattr_empty_first_line.txt │ └── nameattr_invalid_attribute.txt │ ├── narrow-blocks │ ├── narrow-blocks.svg │ └── narrow-blocks.txt │ ├── options │ ├── colordiffusion.svg │ ├── count_name_simple.svg │ ├── count_name_with_symbols.svg │ ├── default.svg │ ├── font_type_cursive.svg │ ├── font_type_fantasy.svg │ ├── font_type_monospace.svg │ ├── font_type_sans-serif.svg │ ├── font_type_serif.svg │ ├── font_type_simple.svg │ ├── font_type_with_quote.svg │ ├── name_type_simple.svg │ ├── name_type_with_backslash.svg │ ├── name_type_with_quote.svg │ ├── notes_simple.svg │ ├── notes_with_symbols.svg │ ├── search_color.svg │ ├── stroke_color.svg │ ├── subtitle_simple.svg │ ├── subtitle_with_symbols.svg │ ├── title_simple.svg │ ├── title_with_symbols.svg │ ├── truncate-right.svg │ └── uicolor_color.svg │ ├── palette-map │ ├── consistent-palette.svg │ ├── palette.map │ └── palette_invalid.map │ ├── perf-vertx-stacks │ ├── perf-vertx-stacks-01-collapsed-all-reversed-stacks.svg │ └── perf-vertx-stacks-01-collapsed-all.svg │ └── unsorted-input │ └── perf-vertx-stacks-01-collapsed-all-unsorted.txt ├── diff-folded.rs └── flamegraph.rs /.codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | range: 85..100 # set a high standard for ourselves 3 | ignore: 4 | - "src/bin" # don't consider binaries in coverage report 5 | - "tests" # we also don't care about coverage of test code 6 | - "benches" # or coverage of benchmarks 7 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Ensure git doesn't change the line-endings of our data files on Windows. 2 | tests/data/**/* binary diff 3 | -------------------------------------------------------------------------------- /.github/DOCS.md: -------------------------------------------------------------------------------- 1 | # Github config and workflows 2 | 3 | In this folder there is configuration for codecoverage, dependabot, and ci 4 | workflows that check the library more deeply than the default configurations. 5 | 6 | This folder can be or was merged using a --allow-unrelated-histories merge 7 | strategy from which provides a 8 | reasonably sensible base for writing your own ci on. By using this strategy 9 | the history of the CI repo is included in your repo, and future updates to 10 | the CI can be merged later. 11 | 12 | To perform this merge run: 13 | 14 | ```shell 15 | git remote add ci https://github.com/jonhoo/rust-ci-conf.git 16 | git fetch ci 17 | git merge --allow-unrelated-histories ci/main 18 | ``` 19 | 20 | An overview of the files in this project is available at: 21 | , which contains some 22 | rationale for decisions and runs through an example of solving minimal version 23 | and OpenSSL issues. 24 | -------------------------------------------------------------------------------- /.github/codecov.yml: -------------------------------------------------------------------------------- 1 | # ref: https://docs.codecov.com/docs/codecovyml-reference 2 | coverage: 3 | # Hold ourselves to a high bar 4 | range: 85..100 5 | round: down 6 | precision: 1 7 | status: 8 | # ref: https://docs.codecov.com/docs/commit-status 9 | project: 10 | default: 11 | # Avoid false negatives 12 | threshold: 1% 13 | 14 | # Test files aren't important for coverage 15 | ignore: 16 | - "tests" 17 | 18 | # Make comments less noisy 19 | comment: 20 | layout: "files" 21 | require_changes: true 22 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: / 5 | schedule: 6 | interval: daily 7 | - package-ecosystem: cargo 8 | directory: / 9 | schedule: 10 | interval: daily 11 | ignore: 12 | - dependency-name: "*" 13 | # patch and minor updates don't matter for libraries as consumers of this library build 14 | # with their own lockfile, rather than the version specified in this library's lockfile 15 | # remove this ignore rule if your package has binaries to ensure that the binaries are 16 | # built with the exact set of dependencies and those are up to date. 17 | update-types: 18 | - "version-update:semver-patch" 19 | - "version-update:semver-minor" 20 | -------------------------------------------------------------------------------- /.github/workflows/check.yml: -------------------------------------------------------------------------------- 1 | # This workflow runs whenever a PR is opened or updated, or a commit is pushed to main. It runs 2 | # several checks: 3 | # - fmt: checks that the code is formatted according to rustfmt 4 | # - clippy: checks that the code does not contain any clippy warnings 5 | # - doc: checks that the code can be documented without errors 6 | # - hack: check combinations of feature flags 7 | # - msrv: check that the msrv specified in the crate is correct 8 | permissions: 9 | contents: read 10 | # This configuration allows maintainers of this repo to create a branch and pull request based on 11 | # the new branch. Restricting the push trigger to the main branch ensures that the PR only gets 12 | # built once. 13 | on: 14 | push: 15 | branches: [main] 16 | pull_request: 17 | # If new code is pushed to a PR branch, then cancel in progress workflows for that PR. Ensures that 18 | # we don't waste CI time, and returns results quicker https://github.com/jonhoo/rust-ci-conf/pull/5 19 | concurrency: 20 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 21 | cancel-in-progress: true 22 | name: check 23 | jobs: 24 | fmt: 25 | runs-on: ubuntu-latest 26 | name: stable / fmt 27 | steps: 28 | - uses: actions/checkout@v4 29 | with: 30 | submodules: true 31 | - name: Install stable 32 | uses: dtolnay/rust-toolchain@stable 33 | with: 34 | components: rustfmt 35 | - name: cargo fmt --check 36 | run: cargo fmt --check 37 | clippy: 38 | runs-on: ubuntu-latest 39 | name: ${{ matrix.toolchain }} / clippy 40 | permissions: 41 | contents: read 42 | checks: write 43 | strategy: 44 | fail-fast: false 45 | matrix: 46 | # Get early warning of new lints which are regularly introduced in beta channels. 47 | toolchain: [stable, beta] 48 | steps: 49 | - uses: actions/checkout@v4 50 | with: 51 | submodules: true 52 | - name: Install ${{ matrix.toolchain }} 53 | uses: dtolnay/rust-toolchain@master 54 | with: 55 | toolchain: ${{ matrix.toolchain }} 56 | components: clippy 57 | - name: cargo clippy 58 | uses: giraffate/clippy-action@v1 59 | with: 60 | reporter: 'github-pr-check' 61 | github_token: ${{ secrets.GITHUB_TOKEN }} 62 | semver: 63 | runs-on: ubuntu-latest 64 | name: semver 65 | steps: 66 | - uses: actions/checkout@v4 67 | with: 68 | submodules: true 69 | - name: Install stable 70 | uses: dtolnay/rust-toolchain@stable 71 | with: 72 | components: rustfmt 73 | - name: cargo-semver-checks 74 | uses: obi1kenobi/cargo-semver-checks-action@v2 75 | doc: 76 | # run docs generation on nightly rather than stable. This enables features like 77 | # https://doc.rust-lang.org/beta/unstable-book/language-features/doc-cfg.html which allows an 78 | # API be documented as only available in some specific platforms. 79 | runs-on: ubuntu-latest 80 | name: nightly / doc 81 | steps: 82 | - uses: actions/checkout@v4 83 | with: 84 | submodules: true 85 | - name: Install nightly 86 | uses: dtolnay/rust-toolchain@nightly 87 | - name: Install cargo-docs-rs 88 | uses: dtolnay/install@cargo-docs-rs 89 | - name: cargo docs-rs 90 | run: cargo docs-rs 91 | hack: 92 | # cargo-hack checks combinations of feature flags to ensure that features are all additive 93 | # which is required for feature unification 94 | runs-on: ubuntu-latest 95 | name: ubuntu / stable / features 96 | steps: 97 | - uses: actions/checkout@v4 98 | with: 99 | submodules: true 100 | - name: Install stable 101 | uses: dtolnay/rust-toolchain@stable 102 | - name: cargo install cargo-hack 103 | uses: taiki-e/install-action@cargo-hack 104 | # intentionally no target specifier; see https://github.com/jonhoo/rust-ci-conf/pull/4 105 | # --feature-powerset runs for every combination of features 106 | - name: cargo hack 107 | run: cargo hack --feature-powerset check 108 | msrv: 109 | # check that we can build using the minimal rust version that is specified by this crate 110 | runs-on: ubuntu-latest 111 | # we use a matrix here just because env can't be used in job names 112 | # https://docs.github.com/en/actions/learn-github-actions/contexts#context-availability 113 | strategy: 114 | matrix: 115 | msrv: ["1.71.0"] # env_logger and clap 116 | name: ubuntu / ${{ matrix.msrv }} 117 | steps: 118 | - uses: actions/checkout@v4 119 | with: 120 | submodules: true 121 | - name: Install ${{ matrix.msrv }} 122 | uses: dtolnay/rust-toolchain@master 123 | with: 124 | toolchain: ${{ matrix.msrv }} 125 | - name: cargo +${{ matrix.msrv }} check 126 | run: cargo check 127 | -------------------------------------------------------------------------------- /.github/workflows/scheduled.yml: -------------------------------------------------------------------------------- 1 | # Run scheduled (rolling) jobs on a nightly basis, as your crate may break independently of any 2 | # given PR. E.g., updates to rust nightly and updates to this crates dependencies. See check.yml for 3 | # information about how the concurrency cancellation and workflow triggering works 4 | permissions: 5 | contents: read 6 | on: 7 | push: 8 | branches: [main] 9 | pull_request: 10 | schedule: 11 | - cron: '7 7 * * *' 12 | concurrency: 13 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 14 | cancel-in-progress: true 15 | name: rolling 16 | jobs: 17 | # https://twitter.com/mycoliza/status/1571295690063753218 18 | nightly: 19 | runs-on: ubuntu-latest 20 | name: ubuntu / nightly 21 | steps: 22 | - uses: actions/checkout@v4 23 | with: 24 | submodules: true 25 | - name: Install nightly 26 | uses: dtolnay/rust-toolchain@nightly 27 | - name: cargo generate-lockfile 28 | if: hashFiles('Cargo.lock') == '' 29 | run: cargo generate-lockfile 30 | - name: cargo test --locked 31 | run: cargo test --locked --all-features --all-targets 32 | # https://twitter.com/alcuadrado/status/1571291687837732873 33 | update: 34 | # This action checks that updating the dependencies of this crate to the latest available that 35 | # satisfy the versions in Cargo.toml does not break this crate. This is important as consumers 36 | # of this crate will generally use the latest available crates. This is subject to the standard 37 | # Cargo semver rules (i.e cargo does not update to a new major version unless explicitly told 38 | # to). 39 | runs-on: ubuntu-latest 40 | name: ubuntu / beta / updated 41 | # There's no point running this if no Cargo.lock was checked in in the first place, since we'd 42 | # just redo what happened in the regular test job. Unfortunately, hashFiles only works in if on 43 | # steps, so we repeat it. 44 | steps: 45 | - uses: actions/checkout@v4 46 | with: 47 | submodules: true 48 | - name: Install beta 49 | if: hashFiles('Cargo.lock') != '' 50 | uses: dtolnay/rust-toolchain@beta 51 | - name: cargo update 52 | if: hashFiles('Cargo.lock') != '' 53 | run: cargo update 54 | - name: cargo test 55 | if: hashFiles('Cargo.lock') != '' 56 | run: cargo test --locked --all-features --all-targets 57 | env: 58 | RUSTFLAGS: -D deprecated 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | /perf* 4 | *.log 5 | /*.svg 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "flamegraph"] 2 | path = flamegraph 3 | url = https://github.com/jonhoo/FlameGraph.git 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "inferno" 3 | version = "0.12.2" 4 | edition = "2021" 5 | authors = ["Jon Gjengset "] 6 | rust-version = "1.71.0" 7 | 8 | readme = "README.md" 9 | description = "Rust port of the FlameGraph performance profiling tool suite" 10 | repository = "https://github.com/jonhoo/inferno.git" 11 | 12 | keywords = ["perf", "flamegraph", "profiling"] 13 | categories = ["command-line-utilities", "development-tools::profiling", "visualization"] 14 | 15 | license = "CDDL-1.0" 16 | 17 | exclude = ["/tests/**", "/flamegraph/**", "/*.perf"] 18 | 19 | [profile.release] 20 | strip = true # To use flamegraph on inferno binaries, comment this line 21 | # debug = true # and uncomment this line. 22 | 23 | [features] 24 | default = ["cli", "multithreaded", "nameattr"] 25 | cli = ["clap", "env_logger"] 26 | multithreaded = ["dashmap", "crossbeam-utils", "crossbeam-channel"] 27 | nameattr = ["indexmap"] 28 | 29 | [dependencies] 30 | ahash = "0.8" 31 | crossbeam-utils = { version = "0.8", optional = true } 32 | crossbeam-channel = { version = "0.5", optional = true } 33 | dashmap = { version = "6.0.1", optional = true } 34 | env_logger = { version = "0.11", default-features = false, optional = true } 35 | indexmap = { version = "2.0", optional = true } 36 | itoa = "1" 37 | log = "0.4" 38 | num-format = { version = "0.4.3", default-features = false } 39 | quick-xml = { version = "0.37", default-features = false } 40 | rgb = "0.8.13" 41 | str_stack = "0.1" 42 | clap = { version = "4.0.1", optional = true, features = ["derive"] } 43 | once_cell = "1.12.0" 44 | 45 | [dev-dependencies] 46 | assert_cmd = "2" 47 | criterion = "0.5" 48 | libflate = "2" 49 | maplit = "1.0.1" 50 | pretty_assertions = "1" 51 | rand = { version = "0.9", features = ["small_rng"] } 52 | serde = { version = "1.0.145" } 53 | testing_logger = "0.1.1" 54 | 55 | # for -Zminimal-versions 56 | [target.'cfg(any())'.dependencies] 57 | # Force criterion to pull in regex 1.6 instead of 1.5 during minimal version CI; 58 | # otherwise compilation fails with... 59 | # ``` 60 | # error[E0433]: failed to resolve: use of undeclared crate or module `syntax` 61 | # --> <$HOME>/.cargo/registry/src/github.com-1ecc6299db9ec823/regex-1.5.0/src/literal/mod.rs:9:9 62 | # | 63 | # 9 | use syntax::hir::literal::Literals; 64 | # | ^^^^^^ use of undeclared crate or module `syntax` 65 | # ``` 66 | # Forcing >= 1.5.1 would be enough to solve this issue, but since regex 1.6.0 67 | # supports our minimum supported rust version of 1.59.0, regex 1.6.x is fine 68 | regex = { version = "1.6", default-features = false, optional = true } 69 | # rle-decode-fast 1.0.0 no longer builds with newer Rust 70 | rle-decode-fast = { version = "1.0.3", default-features = false, optional = true } 71 | 72 | [lib] 73 | name = "inferno" 74 | path = "src/lib.rs" 75 | 76 | [[bin]] 77 | name = "inferno-collapse-perf" 78 | path = "src/bin/collapse-perf.rs" 79 | required-features = ["cli"] 80 | 81 | [[bin]] 82 | name = "inferno-collapse-dtrace" 83 | path = "src/bin/collapse-dtrace.rs" 84 | required-features = ["cli"] 85 | 86 | [[bin]] 87 | name = "inferno-collapse-xctrace" 88 | path = "src/bin/collapse-xctrace.rs" 89 | required-features = ["cli"] 90 | 91 | [[bin]] 92 | name = "inferno-collapse-sample" 93 | path = "src/bin/collapse-sample.rs" 94 | required-features = ["cli"] 95 | 96 | [[bin]] 97 | name = "inferno-collapse-vtune" 98 | path = "src/bin/collapse-vtune.rs" 99 | required-features = ["cli"] 100 | 101 | [[bin]] 102 | name = "inferno-collapse-vsprof" 103 | path = "src/bin/collapse-vsprof.rs" 104 | required-features = ["cli"] 105 | 106 | [[bin]] 107 | name = "inferno-collapse-ghcprof" 108 | path = "src/bin/collapse-ghcprof.rs" 109 | required-features = ["cli"] 110 | 111 | [[bin]] 112 | name = "inferno-collapse-guess" 113 | path = "src/bin/collapse-guess.rs" 114 | required-features = ["cli"] 115 | 116 | [[bin]] 117 | name = "inferno-collapse-recursive" 118 | path = "src/bin/collapse-recursive.rs" 119 | required-features = ["cli"] 120 | 121 | [[bin]] 122 | name = "inferno-flamegraph" 123 | path = "src/bin/flamegraph.rs" 124 | required-features = ["cli"] 125 | 126 | [[bin]] 127 | name = "inferno-diff-folded" 128 | path = "src/bin/diff-folded.rs" 129 | required-features = ["cli"] 130 | 131 | [[bench]] 132 | name = "collapse" 133 | harness = false 134 | required-features = ["multithreaded"] 135 | 136 | [[bench]] 137 | name = "flamegraph" 138 | harness = false 139 | required-features = ["multithreaded"] 140 | -------------------------------------------------------------------------------- /benches/collapse.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::io::{self, Read}; 3 | 4 | use criterion::*; 5 | use inferno::collapse::{dtrace, perf, sample, Collapse}; 6 | use libflate::gzip::Decoder; 7 | use once_cell::sync::Lazy; 8 | 9 | const INFILE_DTRACE: &str = "flamegraph/example-dtrace-stacks.txt"; 10 | const INFILE_PERF: &str = "flamegraph/example-perf-stacks.txt.gz"; 11 | const INFILE_SAMPLE: &str = "tests/data/collapse-sample/large.txt.gz"; 12 | const SAMPLE_SIZE: usize = 100; 13 | 14 | static NTHREADS: Lazy = Lazy::new(|| std::thread::available_parallelism().unwrap().into()); 15 | 16 | fn read_infile(infile: &str, buf: &mut Vec) -> io::Result<()> { 17 | let mut f = File::open(infile)?; 18 | if infile.ends_with(".gz") { 19 | let mut r = io::BufReader::new(Decoder::new(f)?); 20 | r.read_to_end(buf)?; 21 | } else { 22 | f.read_to_end(buf)?; 23 | } 24 | Ok(()) 25 | } 26 | 27 | macro_rules! benchmark_single { 28 | ($name:ident, $name_str:expr, $infile:expr) => { 29 | fn $name(c: &mut Criterion) { 30 | let mut bytes = Vec::new(); 31 | read_infile($infile, &mut bytes).unwrap(); 32 | 33 | let mut collapser = $name::Folder::default(); 34 | 35 | let mut group = c.benchmark_group($name_str); 36 | 37 | group 38 | .bench_with_input("collapse", &bytes, move |b, data| { 39 | b.iter(|| { 40 | let _result = collapser.collapse(data.as_slice(), io::sink()); 41 | }) 42 | }) 43 | .throughput(Throughput::Bytes(bytes.len() as u64)) 44 | .sample_size(SAMPLE_SIZE); 45 | 46 | group.finish(); 47 | } 48 | }; 49 | } 50 | 51 | macro_rules! benchmark_multi { 52 | ($name:ident, $name_str:expr, $infile:expr) => { 53 | fn $name(c: &mut Criterion) { 54 | let mut bytes = Vec::new(); 55 | read_infile($infile, &mut bytes).unwrap(); 56 | 57 | let mut collapser1 = { 58 | let mut options = $name::Options::default(); 59 | options.nthreads = 1; 60 | $name::Folder::from(options) 61 | }; 62 | 63 | let mut collapser2 = { 64 | let mut options = $name::Options::default(); 65 | options.nthreads = *NTHREADS; 66 | $name::Folder::from(options) 67 | }; 68 | 69 | let mut group = c.benchmark_group("collapse"); 70 | 71 | group 72 | .bench_with_input(format!("{}/{}", $name_str, 1), &bytes, move |b, data| { 73 | b.iter(|| { 74 | let _result = collapser1.collapse(data.as_slice(), io::sink()); 75 | }) 76 | }) 77 | .throughput(Throughput::Bytes(bytes.len() as u64)) 78 | .sample_size(SAMPLE_SIZE); 79 | 80 | group 81 | .bench_with_input( 82 | format!("{}/{}", $name_str, *NTHREADS), 83 | &bytes, 84 | move |b, data| { 85 | b.iter(|| { 86 | let _result = collapser2.collapse(data.as_slice(), io::sink()); 87 | }) 88 | }, 89 | ) 90 | .throughput(Throughput::Bytes(bytes.len() as u64)) 91 | .sample_size(SAMPLE_SIZE); 92 | 93 | group.finish(); 94 | } 95 | }; 96 | } 97 | 98 | benchmark_multi!(dtrace, "dtrace", INFILE_DTRACE); 99 | benchmark_multi!(perf, "perf", INFILE_PERF); 100 | benchmark_single!(sample, "sample", INFILE_SAMPLE); 101 | 102 | criterion_group!(benches, dtrace, perf, sample); 103 | 104 | criterion_main!(benches); 105 | -------------------------------------------------------------------------------- /benches/flamegraph.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::io::{self, BufReader, Read}; 3 | 4 | use criterion::*; 5 | use inferno::flamegraph::{self, Options}; 6 | 7 | fn flamegraph_benchmark(c: &mut Criterion, id: &str, infile: &str, mut opt: Options<'static>) { 8 | let mut f = File::open(infile).expect("file not found"); 9 | 10 | let mut bytes = Vec::new(); 11 | f.read_to_end(&mut bytes).expect("Could not read file"); 12 | 13 | let mut group = c.benchmark_group(id); 14 | 15 | group 16 | .bench_with_input("flamegraph", &bytes, move |b, data| { 17 | b.iter(|| { 18 | let reader = BufReader::new(data.as_slice()); 19 | let _folder = flamegraph::from_reader(&mut opt, reader, io::sink()); 20 | }) 21 | }) 22 | .throughput(Throughput::Bytes(bytes.len() as u64)); 23 | 24 | group.finish(); 25 | } 26 | 27 | macro_rules! flamegraph_benchmarks { 28 | ($($name:ident : ($infile:expr, $opt:expr)),*) => { 29 | $( 30 | fn $name(c: &mut Criterion) { 31 | let id = stringify!($name); 32 | flamegraph_benchmark(c, id, $infile, $opt); 33 | } 34 | )* 35 | 36 | criterion_group!(benches, $($name),*); 37 | criterion_main!(benches); 38 | } 39 | } 40 | 41 | flamegraph_benchmarks! { 42 | flamegraph: ("tests/data/collapse-perf/results/example-perf-stacks-collapsed.txt", 43 | { let mut opt = Options::default(); opt.reverse_stack_order = true; opt }) 44 | } 45 | -------------------------------------------------------------------------------- /compare.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eu -o pipefail 4 | BIN="${CARGO_TARGET_DIR:-target}/release/" 5 | 6 | 7 | cargo build --release --bin inferno-collapse-perf 8 | f=flamegraph/example-perf-stacks.txt 9 | zcat < flamegraph/example-perf-stacks.txt.gz > "$f" 10 | echo "==> perf <==" 11 | hyperfine --warmup 20 -m 50 "$BIN/inferno-collapse-perf --all $f" "./flamegraph/stackcollapse-perf.pl --all $f" 12 | rm "$f" 13 | 14 | echo 15 | echo 16 | 17 | cargo build --release --bin inferno-collapse-dtrace 18 | f=flamegraph/example-dtrace-stacks.txt 19 | echo "==> dtrace <==" 20 | hyperfine --warmup 20 -m 50 "$BIN/inferno-collapse-dtrace $f" "./flamegraph/stackcollapse.pl $f" 21 | 22 | echo 23 | echo 24 | 25 | cargo build --release --bin inferno-collapse-sample 26 | f=tests/data/collapse-sample/large.txt 27 | zcat < tests/data/collapse-sample/large.txt.gz > "$f" 28 | echo "==> sample <==" 29 | hyperfine --warmup 20 -m 50 "$BIN/inferno-collapse-sample $f" "./flamegraph/stackcollapse-sample.awk $f" 30 | rm "$f" 31 | -------------------------------------------------------------------------------- /src/bin/collapse-dtrace.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::path::PathBuf; 3 | 4 | use clap::{ArgAction, Parser}; 5 | use env_logger::Env; 6 | use inferno::collapse::dtrace::{Folder, Options}; 7 | use inferno::collapse::{Collapse, DEFAULT_NTHREADS}; 8 | use once_cell::sync::Lazy; 9 | 10 | static NTHREADS: Lazy = Lazy::new(|| DEFAULT_NTHREADS.to_string()); 11 | 12 | #[derive(Debug, Parser)] 13 | #[clap( 14 | name = "inferno-collapse-dtrace", 15 | about, 16 | after_help = "\ 17 | [1] This processes the result of the dtrace ustack() as run with: 18 | dtrace -x ustackframes=100 -n 'profile-97 /pid == 12345 && arg1/ { @[ustack()] = count(); } tick-60s { exit(0); }' 19 | or including kernel time: 20 | dtrace -x ustackframes=100 -n 'profile-97 /pid == 12345/ { @[ustack()] = count(); } tick-60s { exit(0); }' 21 | " 22 | )] 23 | struct Opt { 24 | // ************* // 25 | // *** FLAGS *** // 26 | // ************* // 27 | /// Include offsets 28 | #[clap(long = "includeoffset")] 29 | includeoffset: bool, 30 | 31 | /// Silence all log output 32 | #[clap(short = 'q', long = "quiet")] 33 | quiet: bool, 34 | 35 | /// Verbose logging mode (-v, -vv, -vvv) 36 | #[clap(short = 'v', long = "verbose", action = ArgAction::Count)] 37 | verbose: u8, 38 | 39 | // *************** // 40 | // *** OPTIONS *** // 41 | // *************** // 42 | /// Number of threads to use. 43 | #[clap( 44 | short = 'n', 45 | long = "nthreads", 46 | default_value = &**NTHREADS, 47 | value_name = "UINT" 48 | )] 49 | nthreads: usize, 50 | 51 | // ************ // 52 | // *** ARGS *** // 53 | // ************ // 54 | #[clap(value_name = "PATH")] 55 | /// Dtrace script output file, or STDIN if not specified 56 | infile: Option, 57 | } 58 | 59 | impl Opt { 60 | fn into_parts(self) -> (Option, Options) { 61 | let mut options = Options::default(); 62 | options.includeoffset = self.includeoffset; 63 | options.nthreads = self.nthreads; 64 | (self.infile, options) 65 | } 66 | } 67 | 68 | fn main() -> io::Result<()> { 69 | let opt = Opt::parse(); 70 | 71 | // Initialize logger 72 | if !opt.quiet { 73 | env_logger::Builder::from_env(Env::default().default_filter_or(match opt.verbose { 74 | 0 => "warn", 75 | 1 => "info", 76 | 2 => "debug", 77 | _ => "trace", 78 | })) 79 | .format_timestamp(None) 80 | .init(); 81 | } 82 | 83 | let (infile, options) = opt.into_parts(); 84 | Folder::from(options).collapse_file_to_stdout(infile.as_ref()) 85 | } 86 | -------------------------------------------------------------------------------- /src/bin/collapse-ghcprof.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::path::PathBuf; 3 | 4 | use clap::{ArgAction, ArgGroup, Parser}; 5 | use env_logger::Env; 6 | use inferno::collapse::ghcprof::{Folder, Options, Source}; 7 | use inferno::collapse::Collapse; 8 | 9 | #[derive(Debug, Parser)] 10 | #[clap( 11 | name = "inferno-collapse-ghcprof", 12 | about, 13 | after_help = "\ 14 | [1] This processes the .prof output of GHC (Glasgow Haskell Compiler) 15 | " 16 | )] 17 | #[command(group( 18 | ArgGroup::new("source") 19 | .required(false) 20 | .args(["time", "bytes", "ticks"]), 21 | ))] 22 | struct Opt { 23 | // ************* // 24 | // *** FLAGS *** // 25 | // ************* // 26 | /// Source stack cost centre from the %time column (individual total % of runtime) 27 | /// (This is the default if no cost centre specified) 28 | #[clap(long = "time")] 29 | time: bool, 30 | /// Source stack cost centre from the bytes column (bytes allocated) 31 | #[clap(long = "bytes")] 32 | bytes: bool, 33 | /// Source stack cost centre from the ticks column (runtime ticks) 34 | #[clap(long = "ticks")] 35 | ticks: bool, 36 | 37 | /// Silence all log output 38 | #[clap(short = 'q', long = "quiet")] 39 | quiet: bool, 40 | 41 | /// Verbose logging mode (-v, -vv, -vvv) 42 | #[clap(short = 'v', long = "verbose", action = ArgAction::Count)] 43 | verbose: u8, 44 | 45 | // ************ // 46 | // *** ARGS *** // 47 | // ************ // 48 | /// ghc .prof output file, or STDIN if not specified 49 | #[clap(value_name = "PATH")] 50 | infile: Option, 51 | } 52 | 53 | impl Opt { 54 | fn into_parts(self) -> (Option, Options) { 55 | let mut options = Options::default(); 56 | options.source = if self.ticks { 57 | Source::Ticks 58 | } else if self.bytes { 59 | Source::Bytes 60 | } else { 61 | Source::PercentTime 62 | }; 63 | (self.infile, options) 64 | } 65 | } 66 | 67 | fn main() -> io::Result<()> { 68 | let opt = Opt::parse(); 69 | 70 | // Initialize logger 71 | if !opt.quiet { 72 | env_logger::Builder::from_env(Env::default().default_filter_or(match opt.verbose { 73 | 0 => "warn", 74 | 1 => "info", 75 | 2 => "debug", 76 | _ => "trace", 77 | })) 78 | .format_timestamp(None) 79 | .init(); 80 | } 81 | 82 | let (infile, options) = opt.into_parts(); 83 | Folder::from(options).collapse_file_to_stdout(infile.as_ref()) 84 | } 85 | -------------------------------------------------------------------------------- /src/bin/collapse-guess.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::path::PathBuf; 3 | 4 | use clap::{ArgAction, Parser}; 5 | use env_logger::Env; 6 | use inferno::collapse::guess::{Folder, Options}; 7 | use inferno::collapse::{Collapse, DEFAULT_NTHREADS}; 8 | use once_cell::sync::Lazy; 9 | 10 | static NTHREADS: Lazy = Lazy::new(|| DEFAULT_NTHREADS.to_string()); 11 | 12 | #[derive(Debug, Parser)] 13 | #[clap( 14 | name = "inferno-collapse-guess", 15 | about, 16 | after_help = "\ 17 | [1] Attempts to find an appropriate collapser to use based on the input. 18 | " 19 | )] 20 | struct Opt { 21 | // ************* // 22 | // *** FLAGS *** // 23 | // ************* // 24 | /// Silence all log output 25 | #[clap(short = 'q', long = "quiet")] 26 | quiet: bool, 27 | 28 | /// Verbose logging mode (-v, -vv, -vvv) 29 | #[clap(short = 'v', long = "verbose", action = ArgAction::Count)] 30 | verbose: u8, 31 | 32 | // *************** // 33 | // *** OPTIONS *** // 34 | // *************** // 35 | /// Number of threads to use 36 | #[clap( 37 | short = 'n', 38 | long = "nthreads", 39 | default_value = &**NTHREADS, 40 | value_name = "UINT" 41 | )] 42 | nthreads: usize, 43 | 44 | // ************ // 45 | // *** ARGS *** // 46 | // ************ // 47 | /// Input file, or STDIN if not specified 48 | #[clap(value_name = "PATH")] 49 | infile: Option, 50 | } 51 | 52 | impl Opt { 53 | fn into_parts(self) -> (Option, Options) { 54 | let mut options = Options::default(); 55 | options.nthreads = self.nthreads; 56 | (self.infile, options) 57 | } 58 | } 59 | 60 | fn main() -> io::Result<()> { 61 | let opt = Opt::parse(); 62 | 63 | // Initialize logger 64 | if !opt.quiet { 65 | env_logger::Builder::from_env(Env::default().default_filter_or(match opt.verbose { 66 | 0 => "warn", 67 | 1 => "info", 68 | 2 => "debug", 69 | _ => "trace", 70 | })) 71 | .format_timestamp(None) 72 | .init(); 73 | } 74 | 75 | let (infile, options) = opt.into_parts(); 76 | Folder::from(options).collapse_file_to_stdout(infile.as_ref()) 77 | } 78 | -------------------------------------------------------------------------------- /src/bin/collapse-perf.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::path::PathBuf; 3 | 4 | use clap::{ArgAction, Parser}; 5 | use env_logger::Env; 6 | use inferno::collapse::perf::{Folder, Options}; 7 | use inferno::collapse::{Collapse, DEFAULT_NTHREADS}; 8 | use once_cell::sync::Lazy; 9 | 10 | static NTHREADS: Lazy = Lazy::new(|| DEFAULT_NTHREADS.to_string()); 11 | 12 | #[derive(Debug, Parser)] 13 | #[clap( 14 | name = "inferno-collapse-perf", 15 | about, 16 | after_help = "\ 17 | [1] perf script must emit both PID and TIDs for these to work; eg, Linux < 4.1: 18 | perf script -f comm,pid,tid,cpu,time,event,ip,sym,dso,trace 19 | for Linux >= 4.1: 20 | perf script -F comm,pid,tid,cpu,time,event,ip,sym,dso,trace 21 | If you save this output add --header on Linux >= 3.14 to include perf info." 22 | )] 23 | struct Opt { 24 | // ************* // 25 | // *** FLAGS *** // 26 | // ************* // 27 | /// Include raw addresses where symbols can't be found 28 | #[clap(long = "addrs")] 29 | addrs: bool, 30 | 31 | /// All annotations (--kernel --jit) 32 | #[clap(long = "all")] 33 | all: bool, 34 | 35 | /// Annotate jit functions with a `_[j]` 36 | #[clap(long = "jit")] 37 | jit: bool, 38 | 39 | /// Annotate kernel functions with a `_[k]` 40 | #[clap(long = "kernel")] 41 | kernel: bool, 42 | 43 | /// Include PID with process names 44 | #[clap(long = "pid")] 45 | pid: bool, 46 | 47 | /// Include TID and PID with process names 48 | #[clap(long = "tid")] 49 | tid: bool, 50 | 51 | /// Silence all log output 52 | #[clap(short = 'q', long = "quiet")] 53 | quiet: bool, 54 | 55 | /// Verbose logging mode (-v, -vv, -vvv) 56 | #[clap(short = 'v', long = "verbose", action = ArgAction::Count)] 57 | verbose: u8, 58 | 59 | // *************** // 60 | // *** OPTIONS *** // 61 | // *************** // 62 | /// Event filter [default: first encountered event] 63 | #[clap(long = "event-filter", value_name = "STRING")] 64 | event_filter: Option, 65 | 66 | /// Number of threads to use 67 | #[clap( 68 | short = 'n', 69 | long = "nthreads", 70 | default_value = &**NTHREADS, 71 | value_name = "UINT" 72 | )] 73 | nthreads: usize, 74 | 75 | // ************ // 76 | // *** ARGS *** // 77 | // ************ // 78 | #[clap(value_name = "PATH")] 79 | /// Perf script output file, or STDIN if not specified 80 | infile: Option, 81 | 82 | #[clap(long = "skip-after", value_name = "STRING")] 83 | /// If set, will omit all the parent stack frames of any frame with a matched function name. 84 | /// 85 | /// Has no effect on the stack trace if no functions are matched. 86 | skip_after: Vec, 87 | } 88 | 89 | impl Opt { 90 | fn into_parts(self) -> (Option, Options) { 91 | let mut options = Options::default(); 92 | options.include_pid = self.pid; 93 | options.include_tid = self.tid; 94 | options.include_addrs = self.addrs; 95 | options.annotate_jit = self.jit || self.all; 96 | options.annotate_kernel = self.kernel || self.all; 97 | options.event_filter = self.event_filter; 98 | options.nthreads = self.nthreads; 99 | options.skip_after = self.skip_after; 100 | (self.infile, options) 101 | } 102 | } 103 | 104 | fn main() -> io::Result<()> { 105 | let opt = Opt::parse(); 106 | 107 | // Initialize logger 108 | if !opt.quiet { 109 | env_logger::Builder::from_env(Env::default().default_filter_or(match opt.verbose { 110 | 0 => "warn", 111 | 1 => "info", 112 | 2 => "debug", 113 | _ => "trace", 114 | })) 115 | .format_timestamp(None) 116 | .init(); 117 | } 118 | 119 | let (infile, options) = opt.into_parts(); 120 | Folder::from(options).collapse_file_to_stdout(infile.as_ref()) 121 | } 122 | -------------------------------------------------------------------------------- /src/bin/collapse-recursive.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::path::PathBuf; 3 | 4 | use clap::Parser; 5 | use inferno::collapse::recursive::{Folder, Options}; 6 | use inferno::collapse::{Collapse, DEFAULT_NTHREADS}; 7 | use once_cell::sync::Lazy; 8 | 9 | static NTHREADS: Lazy = Lazy::new(|| DEFAULT_NTHREADS.to_string()); 10 | 11 | #[derive(Debug, Parser)] 12 | #[clap(name = "inferno-collapse-recursive", about)] 13 | struct Opt { 14 | /// Number of threads to use 15 | #[clap( 16 | short = 'n', 17 | long = "nthreads", 18 | default_value = &**NTHREADS, 19 | value_name = "UINT" 20 | )] 21 | nthreads: usize, 22 | 23 | #[clap(value_name = "PATH")] 24 | /// Collapse output file, or STDIN if not specified 25 | infile: Option, 26 | } 27 | 28 | impl Opt { 29 | fn into_parts(self) -> (Option, Options) { 30 | let mut options = Options::default(); 31 | options.nthreads = self.nthreads; 32 | (self.infile, options) 33 | } 34 | } 35 | 36 | fn main() -> io::Result<()> { 37 | let opt = Opt::parse(); 38 | let (infile, options) = opt.into_parts(); 39 | Folder::from(options).collapse_file_to_stdout(infile.as_ref()) 40 | } 41 | -------------------------------------------------------------------------------- /src/bin/collapse-sample.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::path::PathBuf; 3 | 4 | use clap::{ArgAction, Parser}; 5 | use env_logger::Env; 6 | use inferno::collapse::sample::{Folder, Options}; 7 | use inferno::collapse::Collapse; 8 | 9 | #[derive(Debug, Parser)] 10 | #[clap( 11 | name = "inferno-collapse-sample", 12 | about, 13 | after_help = "\ 14 | [1] This processes the result of the sample command on macOS: 15 | sample 1234 -file out.sample_stacks" 16 | )] 17 | struct Opt { 18 | // ************* // 19 | // *** FLAGS *** // 20 | // ************* // 21 | /// Don't include modules with function names 22 | #[clap(long = "no-modules")] 23 | no_modules: bool, 24 | 25 | /// Silence all log output 26 | #[clap(short = 'q', long = "quiet")] 27 | quiet: bool, 28 | 29 | /// Verbose logging mode (-v, -vv, -vvv) 30 | #[clap(short = 'v', long = "verbose", action = ArgAction::Count)] 31 | verbose: u8, 32 | 33 | // ************ // 34 | // *** ARGS *** // 35 | // ************ // 36 | /// sample output file, or STDIN if not specified 37 | #[clap(value_name = "PATH")] 38 | infile: Option, 39 | } 40 | 41 | impl Opt { 42 | fn into_parts(self) -> (Option, Options) { 43 | let mut options = Options::default(); 44 | options.no_modules = self.no_modules; 45 | (self.infile, options) 46 | } 47 | } 48 | 49 | fn main() -> io::Result<()> { 50 | let opt = Opt::parse(); 51 | 52 | // Initialize logger 53 | if !opt.quiet { 54 | env_logger::Builder::from_env(Env::default().default_filter_or(match opt.verbose { 55 | 0 => "warn", 56 | 1 => "info", 57 | 2 => "debug", 58 | _ => "trace", 59 | })) 60 | .format_timestamp(None) 61 | .init(); 62 | } 63 | 64 | let (infile, options) = opt.into_parts(); 65 | Folder::from(options).collapse_file_to_stdout(infile.as_ref()) 66 | } 67 | -------------------------------------------------------------------------------- /src/bin/collapse-vsprof.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::path::PathBuf; 3 | 4 | use clap::{ArgAction, Parser}; 5 | use env_logger::Env; 6 | use inferno::collapse::vsprof::Folder; 7 | use inferno::collapse::Collapse; 8 | 9 | #[derive(Debug, Parser)] 10 | #[clap( 11 | name = "inferno-collapse-vsprof", 12 | about, 13 | after_help = "\ 14 | [1] This processes the call tree summary of the built in Visual Studio profiler" 15 | )] 16 | struct Opt { 17 | // ************* // 18 | // *** FLAGS *** // 19 | // ************* // 20 | /// Silence all log output 21 | #[clap(short = 'q', long = "quiet")] 22 | quiet: bool, 23 | 24 | /// Verbose logging mode (-v, -vv, -vvv) 25 | #[clap(short = 'v', long = "verbose", action = ArgAction::Count)] 26 | verbose: u8, 27 | 28 | // ************ // 29 | // *** ARGS *** // 30 | // ************ // 31 | #[clap(value_name = "PATH")] 32 | /// Call tree summary file from the built in Visual Studio profiler, or STDIN if not specified 33 | infile: Option, 34 | } 35 | 36 | fn main() -> io::Result<()> { 37 | let opt = Opt::parse(); 38 | 39 | // Initialize logger 40 | if !opt.quiet { 41 | env_logger::Builder::from_env(Env::default().default_filter_or(match opt.verbose { 42 | 0 => "warn", 43 | 1 => "info", 44 | 2 => "debug", 45 | _ => "trace", 46 | })) 47 | .format_timestamp(None) 48 | .init(); 49 | } 50 | 51 | Folder::default().collapse_file_to_stdout(opt.infile) 52 | } 53 | -------------------------------------------------------------------------------- /src/bin/collapse-vtune.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::path::PathBuf; 3 | 4 | use clap::{ArgAction, Parser}; 5 | use env_logger::Env; 6 | use inferno::collapse::vtune::{Folder, Options}; 7 | use inferno::collapse::Collapse; 8 | 9 | #[derive(Debug, Parser)] 10 | #[clap( 11 | name = "inferno-collapse-vtune", 12 | about, 13 | after_help = "\ 14 | [1] This processes the CSV output of the Intel VTune `amplxe-cl` tool, created as follows: 15 | amplxe-cl -collect hotspots -r -- 16 | amplxe-cl -R top-down -call-stack-mode all -column=\"CPU Time:Self\",\"Module\" -report-out result.csv -filter \"Function Stack\" -format csv -csv-delimiter comma -r 17 | " 18 | )] 19 | struct Opt { 20 | // ************* // 21 | // *** FLAGS *** // 22 | // ************* // 23 | /// Don't include modules with function names 24 | #[clap(long = "no-modules")] 25 | no_modules: bool, 26 | 27 | /// Silence all log output 28 | #[clap(short = 'q', long = "quiet")] 29 | quiet: bool, 30 | 31 | /// Verbose logging mode (-v, -vv, -vvv) 32 | #[clap(short = 'v', long = "verbose", action = ArgAction::Count)] 33 | verbose: u8, 34 | 35 | // ************ // 36 | // *** ARGS *** // 37 | // ************ // 38 | /// VTune CSV output file, or STDIN if not specified 39 | #[clap(value_name = "PATH")] 40 | infile: Option, 41 | } 42 | 43 | impl Opt { 44 | fn into_parts(self) -> (Option, Options) { 45 | let mut options = Options::default(); 46 | options.no_modules = self.no_modules; 47 | (self.infile, options) 48 | } 49 | } 50 | 51 | fn main() -> io::Result<()> { 52 | let opt = Opt::parse(); 53 | 54 | // Initialize logger 55 | if !opt.quiet { 56 | env_logger::Builder::from_env(Env::default().default_filter_or(match opt.verbose { 57 | 0 => "warn", 58 | 1 => "info", 59 | 2 => "debug", 60 | _ => "trace", 61 | })) 62 | .format_timestamp(None) 63 | .init(); 64 | } 65 | 66 | let (infile, options) = opt.into_parts(); 67 | Folder::from(options).collapse_file_to_stdout(infile.as_ref()) 68 | } 69 | -------------------------------------------------------------------------------- /src/bin/collapse-xctrace.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::path::PathBuf; 3 | 4 | use clap::{ArgAction, Parser}; 5 | use env_logger::Env; 6 | use inferno::collapse::xctrace::Folder; 7 | use inferno::collapse::Collapse; 8 | 9 | #[derive(Debug, Parser)] 10 | #[clap( 11 | name = "inferno-collapse-xctrace", 12 | about, 13 | after_help = r#"\ 14 | [1] This processes the result of the xctrace with `Timer Profiler` profile as run with: 15 | `xctrace record --template 'Time Profiler' --launch --output tmp.trace` 16 | or 17 | `xctrace record --template 'Time Profiler' --attach --output tmp.trace` 18 | then 19 | xctrace export --input tmp.trace --xpath '/trace-toc/*/data/table[@schema="time-profile"]' > tmp.xml 20 | "# 21 | )] 22 | struct Opt { 23 | /// Silence all log output 24 | #[clap(short = 'q', long = "quiet")] 25 | quiet: bool, 26 | 27 | /// Verbose logging mode (-v, -vv, -vvv) 28 | #[clap(short = 'v', long = "verbose", action = ArgAction::Count)] 29 | verbose: u8, 30 | 31 | // ************ // 32 | // *** ARGS *** // 33 | // ************ // 34 | /// xctrace output file, or STDIN if not specified 35 | #[clap(value_name = "PATH")] 36 | infile: Option, 37 | } 38 | 39 | fn main() -> io::Result<()> { 40 | let opt = Opt::parse(); 41 | 42 | // Initialize logger 43 | if !opt.quiet { 44 | env_logger::Builder::from_env(Env::default().default_filter_or(match opt.verbose { 45 | 0 => "warn", 46 | 1 => "info", 47 | 2 => "debug", 48 | _ => "trace", 49 | })) 50 | .format_timestamp(None) 51 | .init(); 52 | } 53 | 54 | Folder::default().collapse_file_to_stdout(opt.infile.as_ref()) 55 | } 56 | -------------------------------------------------------------------------------- /src/bin/diff-folded.rs: -------------------------------------------------------------------------------- 1 | use std::io::{self, IsTerminal}; 2 | use std::path::PathBuf; 3 | 4 | use clap::{ArgAction, Parser}; 5 | use env_logger::Env; 6 | use inferno::differential::{self, Options}; 7 | 8 | #[derive(Debug, Parser)] 9 | #[clap( 10 | name = "inferno-diff-folded", 11 | about, 12 | after_help = "\ 13 | Creates a differential between two folded stack profiles that can be passed 14 | to inferno-flamegraph to generate a differential flame graph. 15 | 16 | $ inferno-diff-folded folded1 folded2 | inferno-flamegraph > diff2.svg 17 | 18 | The flamegraph will be colored based on higher samples (red) and smaller 19 | samples (blue). The frame widths will be based on the 2nd folded profile. 20 | This might be confusing if stack frames disappear entirely; it will make 21 | the most sense to ALSO create a differential based on the 1st profile widths, 22 | while switching the hues. To do this, reverse the order of the folded files 23 | and pass the --negate flag to inferno-flamegraph like this: 24 | 25 | $ inferno-diff-folded folded2 folded1 | inferno-flamegraph --negate > diff1.svg 26 | 27 | You can use the inferno-collapse-* tools to generate the folded files." 28 | )] 29 | struct Opt { 30 | // ************* // 31 | // *** FLAGS *** // 32 | // ************* // 33 | /// Normalize sample counts 34 | #[clap(short = 'n', long = "normalize")] 35 | normalize: bool, 36 | 37 | /// Strip hex numbers (addresses) 38 | #[clap(short = 's', long = "strip-hex")] 39 | strip_hex: bool, 40 | 41 | /// Silence all log output 42 | #[clap(short = 'q', long = "quiet")] 43 | quiet: bool, 44 | 45 | /// Verbose logging mode (-v, -vv, -vvv) 46 | #[clap(short = 'v', long = "verbose", action = ArgAction::Count)] 47 | verbose: u8, 48 | 49 | // ************ // 50 | // *** ARGS *** // 51 | // ************ // 52 | /// Path to folded stack profile 1 53 | #[clap(value_name = "PATH1")] 54 | path1: PathBuf, 55 | 56 | /// Path to folded stack profile 2 57 | #[clap(value_name = "PATH2")] 58 | path2: PathBuf, 59 | } 60 | 61 | impl Opt { 62 | fn into_parts(self) -> (PathBuf, PathBuf, Options) { 63 | ( 64 | self.path1, 65 | self.path2, 66 | Options { 67 | normalize: self.normalize, 68 | strip_hex: self.strip_hex, 69 | }, 70 | ) 71 | } 72 | } 73 | 74 | fn main() -> io::Result<()> { 75 | let opt = Opt::parse(); 76 | 77 | // Initialize logger 78 | if !opt.quiet { 79 | env_logger::Builder::from_env(Env::default().default_filter_or(match opt.verbose { 80 | 0 => "warn", 81 | 1 => "info", 82 | 2 => "debug", 83 | _ => "trace", 84 | })) 85 | .format_timestamp(None) 86 | .init(); 87 | } 88 | 89 | let (folded1, folded2, options) = opt.into_parts(); 90 | 91 | if std::io::stdout().is_terminal() { 92 | differential::from_files(options, folded1, folded2, io::stdout().lock()) 93 | } else { 94 | differential::from_files( 95 | options, 96 | folded1, 97 | folded2, 98 | io::BufWriter::new(io::stdout().lock()), 99 | ) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/collapse/guess.rs: -------------------------------------------------------------------------------- 1 | use std::io::prelude::*; 2 | use std::io::{self, Cursor}; 3 | 4 | use log::{error, info}; 5 | 6 | use crate::collapse::{self, dtrace, ghcprof, perf, sample, vsprof, vtune, xctrace, Collapse}; 7 | 8 | const LINES_PER_ITERATION: usize = 10; 9 | 10 | /// Folder configuration options. 11 | #[derive(Clone, Debug)] 12 | #[non_exhaustive] 13 | pub struct Options { 14 | /// The number of threads to use. 15 | /// 16 | /// Default is the number of logical cores on your machine. 17 | pub nthreads: usize, 18 | } 19 | 20 | impl Default for Options { 21 | fn default() -> Self { 22 | Self { 23 | nthreads: *collapse::DEFAULT_NTHREADS, 24 | } 25 | } 26 | } 27 | 28 | /// A collapser that tries to find an appropriate implementation of `Collapse` 29 | /// based on the input, then delegates to that collapser if one is found. 30 | /// 31 | /// If no applicable collapser is found, an error will be logged and 32 | /// nothing will be written. 33 | #[derive(Clone)] 34 | pub struct Folder { 35 | opt: Options, 36 | } 37 | 38 | impl From for Folder { 39 | fn from(opt: Options) -> Self { 40 | Self { opt } 41 | } 42 | } 43 | 44 | impl Default for Folder { 45 | fn default() -> Self { 46 | Options::default().into() 47 | } 48 | } 49 | 50 | impl Collapse for Folder { 51 | fn collapse(&mut self, mut reader: R, writer: W) -> io::Result<()> 52 | where 53 | R: io::BufRead, 54 | W: io::Write, 55 | { 56 | let mut dtrace = { 57 | let options = dtrace::Options { 58 | nthreads: self.opt.nthreads, 59 | ..Default::default() 60 | }; 61 | dtrace::Folder::from(options) 62 | }; 63 | let mut perf = { 64 | let options = perf::Options { 65 | nthreads: self.opt.nthreads, 66 | ..Default::default() 67 | }; 68 | perf::Folder::from(options) 69 | }; 70 | let mut sample = sample::Folder::default(); 71 | let mut vtune = vtune::Folder::default(); 72 | let mut vsprof = vsprof::Folder::default(); 73 | let mut xctrace = xctrace::Folder::default(); 74 | let mut ghcprof = ghcprof::Folder::default(); 75 | 76 | // Each Collapse impl gets its own flag in this array. 77 | // It gets set to true when the impl has been ruled out. 78 | let mut not_applicable = [false; 7]; 79 | 80 | let mut buffer = String::new(); 81 | loop { 82 | let mut eof = false; 83 | for _ in 0..LINES_PER_ITERATION { 84 | if reader.read_line(&mut buffer)? == 0 { 85 | eof = true; 86 | } 87 | } 88 | 89 | macro_rules! try_collapse_impl { 90 | ($collapse:ident, $index:expr) => { 91 | if !not_applicable[$index] { 92 | match $collapse.is_applicable(&buffer) { 93 | Some(false) => { 94 | // We can rule this collapser out. 95 | not_applicable[$index] = true; 96 | } 97 | Some(true) => { 98 | // We found a collapser that works! Let's use it. 99 | info!("Using {} collapser", stringify!($collapse)); 100 | let cursor = Cursor::new(buffer).chain(reader); 101 | return $collapse.collapse(cursor, writer); 102 | } 103 | None => (), // We're not yet sure if this collapser is appropriate 104 | } 105 | } 106 | }; 107 | } 108 | try_collapse_impl!(perf, 0); 109 | try_collapse_impl!(dtrace, 1); 110 | try_collapse_impl!(sample, 2); 111 | try_collapse_impl!(vtune, 3); 112 | try_collapse_impl!(vsprof, 4); 113 | try_collapse_impl!(ghcprof, 5); 114 | try_collapse_impl!(xctrace, 6); 115 | 116 | if eof { 117 | break; 118 | } 119 | } 120 | 121 | error!("No applicable collapse implementation found for input"); 122 | 123 | Ok(()) 124 | } 125 | 126 | fn is_applicable(&mut self, _line: &str) -> Option { 127 | unreachable!() 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/collapse/matcher.rs: -------------------------------------------------------------------------------- 1 | // Detects vmlinux in stack, with version or without. 2 | // 3 | // Examples: 4 | // 5 | // ffffffffb94000e0 __softirqentry_text_start+0xe0 (/usr/lib/debug/boot/vmlinux-5.4.14-cloudflare-2020.1.11) 6 | // 8c3453 tcp_sendmsg (/lib/modules/4.3.0-rc1-virtual/build/vmlinux) 7 | // 7d8 ipv4_conntrack_local+0x7f8f80b8 ([nf_conntrack_ipv4]) 8 | // 9 | #[inline] 10 | pub(super) fn is_vmlinux(s: &str) -> bool { 11 | if let Some(vm) = s.rfind("vmlinux") { 12 | s[vm..] 13 | .chars() 14 | .all(|c| c.is_ascii_alphanumeric() || matches!(c, '-' | '.' | '_')) 15 | } else { 16 | false 17 | } 18 | } 19 | 20 | // Detect kernel from module name, module file or from vmlinux 21 | #[inline] 22 | pub(super) fn is_kernel(s: &str) -> bool { 23 | (s.starts_with('[') || s.ends_with(".ko") || is_vmlinux(s)) && s != "[unknown]" 24 | } 25 | 26 | #[cfg(test)] 27 | mod tests { 28 | use super::*; 29 | 30 | #[test] 31 | fn is_vmlinux_true() { 32 | assert!(is_vmlinux("vmlinux")); 33 | assert!(is_vmlinux("vmlinux-5")); 34 | assert!(is_vmlinux("vmlinux-54")); 35 | assert!(is_vmlinux("vmlinux_54")); 36 | assert!(is_vmlinux("vmlinux-vmlinux")); 37 | assert!(is_vmlinux("vmlinux-5.4.14")); 38 | assert!(is_vmlinux("vmlinux-54-2020")); 39 | assert!(is_vmlinux("vmlinux-cloudflare")); 40 | assert!(is_vmlinux("vmlinux_cloudflare")); 41 | assert!(is_vmlinux("vmlinux-cloudflare-2020.1.11")); 42 | assert!(is_vmlinux("vmlinux-5.4.14-cloudflare-2020.1.11")); 43 | assert!(is_vmlinux("/usr/lib/debug/boot/vmlinux")); 44 | assert!(is_vmlinux( 45 | "/usr/lib/debug/boot/vmlinux-5.4.14-cloudflare-2020.1.11" 46 | )); 47 | } 48 | 49 | #[test] 50 | fn is_vmlinux_false() { 51 | assert!(!is_vmlinux("vmlinux/")); 52 | assert!(!is_vmlinux("vmlinux ")); 53 | assert!(!is_vmlinux("vmlinux+5")); 54 | assert!(!is_vmlinux("vmlinux,54")); 55 | assert!(!is_vmlinux("vmlinux\\5.4.14")); 56 | assert!(!is_vmlinux("vmlinux-Тест")); 57 | assert!(!is_vmlinux("vmlinux-cloudflare ")); 58 | assert!(!is_vmlinux("vmlinux-5.4.14-cloudflare-2020.1.11)")); 59 | assert!(!is_vmlinux("/usr/lib/debug/boot/vmlinu")); 60 | assert!(!is_vmlinux( 61 | "/usr/lib/debug/boot/vmlinu-5.4.14-cloudflare-2020.1.11" 62 | )); 63 | } 64 | 65 | #[test] 66 | fn is_kernel_true() { 67 | assert!(is_kernel("[")); 68 | assert!(is_kernel("[vmlinux")); 69 | assert!(is_kernel("[test")); 70 | assert!(is_kernel("[test]")); 71 | assert!(is_kernel(".ko")); 72 | assert!(is_kernel("module.ko")); 73 | assert!(is_kernel("vmlinux.ko")); 74 | assert!(is_kernel("vmlinux")); 75 | assert!(is_kernel(" [vmlinux")); 76 | assert!(is_kernel("vmlinux-5.4.14-cloudflare-2020.1.11")); 77 | assert!(is_kernel("vmlinux-5.4.14-cloudflare-2020.1.11")); 78 | assert!(is_kernel( 79 | "/usr/lib/debug/boot/vmlinux-5.4.14-cloudflare-2020.1.11" 80 | )); 81 | } 82 | 83 | #[test] 84 | fn is_kernel_false() { 85 | assert!(!is_kernel("[unknown]")); 86 | assert!(!is_kernel(" [")); 87 | assert!(!is_kernel(".ko ")); 88 | assert!(!is_kernel(" [.ko ")); 89 | assert!(!is_kernel("vmlinux-cloudflare ")); 90 | assert!(!is_kernel("vmlinux-5.4.14-cloudflare-2020.1.11)")); 91 | assert!(!is_kernel("/usr/lib/debug/boot/vmlinu")); 92 | assert!(!is_kernel( 93 | "/usr/lib/debug/boot/vmlinu-5.4.14-cloudflare-2020.1.11" 94 | )); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/collapse/mod.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | pub(crate) mod common; 3 | 4 | /// Stack collapsing for the output of [`dtrace`](https://www.joyent.com/dtrace). 5 | /// 6 | /// See the [crate-level documentation] for details. 7 | /// 8 | /// [crate-level documentation]: ../../index.html 9 | pub mod dtrace; 10 | 11 | /// Attempts to use whichever Collapse implementation is appropriate for a given input 12 | pub mod guess; 13 | 14 | /// Stack collapsing for the output of [`perf script`](https://linux.die.net/man/1/perf-script). 15 | /// 16 | /// See the [crate-level documentation] for details. 17 | /// 18 | /// [crate-level documentation]: ../../index.html 19 | pub mod perf; 20 | 21 | /// Internal string match helper functions for perf 22 | pub(crate) mod matcher; 23 | 24 | /// Stack collapsing for the output of [`sample`](https://gist.github.com/loderunner/36724cc9ee8db66db305#profiling-with-sample) on macOS. 25 | /// 26 | /// See the [crate-level documentation] for details. 27 | /// 28 | /// [crate-level documentation]: ../../index.html 29 | pub mod sample; 30 | 31 | /// Stack collapsing for the output of [`VTune`](https://software.intel.com/en-us/vtune-amplifier-help-command-line-interface). 32 | /// 33 | /// See the [crate-level documentation] for details. 34 | /// 35 | /// [crate-level documentation]: ../../index.html 36 | pub mod vtune; 37 | 38 | /// Collapse direct recursive backtraces. 39 | /// 40 | /// Post-process a stack list and merge direct recursive calls. 41 | /// 42 | /// For example, collapses 43 | /// ```text 44 | /// main;recursive;recursive;recursive;helper 1 45 | /// ``` 46 | /// into 47 | /// ```text 48 | /// main;recursive;helper 1 49 | /// ``` 50 | /// 51 | /// See the [crate-level documentation] for details. 52 | /// 53 | /// [crate-level documentation]: ../../index.html 54 | pub mod recursive; 55 | 56 | /// Stack collapsing for the output of the [Visual Studio built-in profiler](https://docs.microsoft.com/en-us/visualstudio/profiling/profiling-feature-tour?view=vs-2019). 57 | /// 58 | /// See the [crate-level documentation] for details. 59 | /// 60 | /// [crate-level documentation]: ../../index.html 61 | pub mod vsprof; 62 | 63 | /// Stack collapsing for the output of the [xctrace](https://developer.apple.com/xcode/features/). 64 | /// 65 | /// See the [crate-level documentation] for details. 66 | /// 67 | /// [crate-level documentation]: ../../index.html 68 | pub mod xctrace; 69 | 70 | /// Stack collapsing for the output of the [GHC's built-in profiler](https://downloads.haskell.org/ghc/latest/docs/users_guide/profiling.html). 71 | /// 72 | /// See the [crate-level documentation] for details. 73 | /// 74 | /// [crate-level documentation]: ../../index.html 75 | pub mod ghcprof; 76 | 77 | // DEFAULT_NTHREADS is public because we use it in the help text of the binaries, 78 | // but it doesn't need to be exposed to library users, hence #[doc(hidden)]. 79 | #[doc(hidden)] 80 | pub use self::common::DEFAULT_NTHREADS; 81 | 82 | use std::fs::File; 83 | use std::io::{self, IsTerminal}; 84 | use std::path::Path; 85 | 86 | use self::common::{CollapsePrivate, CAPACITY_READER}; 87 | 88 | /// The abstract behavior of stack collapsing. 89 | /// 90 | /// Implementors of this trait are providing a way to take the stack traces produced by a 91 | /// particular profiler's output (like `perf script`) and produce lines in the folded stack format 92 | /// expected by [`crate::flamegraph::from_lines`]. 93 | /// 94 | /// See also the [crate-level documentation] for details. 95 | /// 96 | /// [crate-level documentation]: ../index.html 97 | // https://github.com/rust-lang/rust/issues/45040 98 | // #[doc(spotlight)] 99 | pub trait Collapse { 100 | /// Collapses the contents of the provided `reader` and writes folded stack lines to the 101 | /// provided `writer`. 102 | fn collapse(&mut self, reader: R, writer: W) -> io::Result<()> 103 | where 104 | R: io::BufRead, 105 | W: io::Write; 106 | 107 | /// Collapses the contents of the provided file (or of STDIN if `infile` is `None`) and 108 | /// writes folded stack lines to provided `writer`. 109 | fn collapse_file(&mut self, infile: Option

, writer: W) -> io::Result<()> 110 | where 111 | P: AsRef, 112 | W: io::Write, 113 | { 114 | match infile { 115 | Some(ref path) => { 116 | let file = File::open(path)?; 117 | let reader = io::BufReader::with_capacity(CAPACITY_READER, file); 118 | self.collapse(reader, writer) 119 | } 120 | None => { 121 | let stdin = io::stdin(); 122 | let stdin_guard = stdin.lock(); 123 | let reader = io::BufReader::with_capacity(CAPACITY_READER, stdin_guard); 124 | self.collapse(reader, writer) 125 | } 126 | } 127 | } 128 | 129 | /// Collapses the contents of the provided file (or of STDIN if `infile` is `None`) and 130 | /// writes folded stack lines to STDOUT. 131 | fn collapse_file_to_stdout

(&mut self, infile: Option

) -> io::Result<()> 132 | where 133 | P: AsRef, 134 | { 135 | if std::io::stdout().is_terminal() { 136 | self.collapse_file(infile, io::stdout().lock()) 137 | } else { 138 | self.collapse_file(infile, io::BufWriter::new(io::stdout().lock())) 139 | } 140 | } 141 | 142 | /// Returns whether this implementation is appropriate for the given input. 143 | /// 144 | /// - `None` means "not sure -- need more input" 145 | /// - `Some(true)` means "yes, this implementation should work with this string" 146 | /// - `Some(false)` means "no, this implementation definitely won't work" 147 | #[allow(clippy::wrong_self_convention)] 148 | fn is_applicable(&mut self, input: &str) -> Option; 149 | } 150 | 151 | impl Collapse for T 152 | where 153 | T: CollapsePrivate, 154 | { 155 | fn collapse(&mut self, reader: R, writer: W) -> io::Result<()> 156 | where 157 | R: io::BufRead, 158 | W: io::Write, 159 | { 160 | ::collapse(self, reader, writer) 161 | } 162 | 163 | fn is_applicable(&mut self, input: &str) -> Option { 164 | ::is_applicable(self, input) 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/collapse/recursive.rs: -------------------------------------------------------------------------------- 1 | use super::common::{self, CollapsePrivate}; 2 | use std::{borrow::Cow, io}; 3 | 4 | /// Recursive backtrace folder configuration options. 5 | #[derive(Clone, Debug)] 6 | #[non_exhaustive] 7 | pub struct Options { 8 | /// The number of threads to use. 9 | /// 10 | /// Default is the number of logical cores on your machine. 11 | pub nthreads: usize, 12 | } 13 | 14 | impl Default for Options { 15 | fn default() -> Self { 16 | Self { 17 | nthreads: *common::DEFAULT_NTHREADS, 18 | } 19 | } 20 | } 21 | 22 | /// A "middleware" folder that receives and outputs the folded stack format 23 | /// expected by [`crate::flamegraph::from_lines`], collapsing direct recursive 24 | /// backtraces. 25 | #[derive(Clone)] 26 | pub struct Folder { 27 | /// The number of stacks per job to send to the threadpool. 28 | nstacks_per_job: usize, 29 | 30 | // Options... 31 | opt: Options, 32 | } 33 | 34 | impl From for Folder { 35 | fn from(mut opt: Options) -> Self { 36 | if opt.nthreads == 0 { 37 | opt.nthreads = 1; 38 | } 39 | Self { 40 | nstacks_per_job: common::DEFAULT_NSTACKS_PER_JOB, 41 | opt, 42 | } 43 | } 44 | } 45 | 46 | impl Default for Folder { 47 | fn default() -> Self { 48 | Options::default().into() 49 | } 50 | } 51 | 52 | impl CollapsePrivate for Folder { 53 | fn pre_process( 54 | &mut self, 55 | _reader: &mut R, 56 | _occurrences: &mut super::common::Occurrences, 57 | ) -> std::io::Result<()> 58 | where 59 | R: std::io::BufRead, 60 | { 61 | // Don't expect any header. 62 | Ok(()) 63 | } 64 | 65 | fn collapse_single_threaded( 66 | &mut self, 67 | reader: R, 68 | occurrences: &mut super::common::Occurrences, 69 | ) -> std::io::Result<()> 70 | where 71 | R: std::io::BufRead, 72 | { 73 | for line in reader.lines() { 74 | let line = line?; 75 | let (stack, count) = Self::line_parts(&line) 76 | .ok_or_else(|| io::Error::from(io::ErrorKind::InvalidData))?; 77 | 78 | occurrences.insert_or_add(Self::collapse_stack(stack.into()).into_owned(), count); 79 | } 80 | Ok(()) 81 | } 82 | 83 | fn would_end_stack(&mut self, _line: &[u8]) -> bool { 84 | // For our purposes, every line is an independent stack 85 | true 86 | } 87 | 88 | fn clone_and_reset_stack_context(&self) -> Self { 89 | self.clone() 90 | } 91 | 92 | fn is_applicable(&mut self, _input: &str) -> Option { 93 | // It seems doubtful that the user would ever want to guess to collapse 94 | // recursive traces, so let's just never consider ourselves applicable. 95 | Some(false) 96 | } 97 | 98 | fn nstacks_per_job(&self) -> usize { 99 | self.nstacks_per_job 100 | } 101 | 102 | fn set_nstacks_per_job(&mut self, n: usize) { 103 | self.nstacks_per_job = n; 104 | } 105 | 106 | fn nthreads(&self) -> usize { 107 | self.opt.nthreads 108 | } 109 | 110 | fn set_nthreads(&mut self, n: usize) { 111 | self.opt.nthreads = n; 112 | } 113 | } 114 | 115 | impl Folder { 116 | fn line_parts(line: &str) -> Option<(&str, usize)> { 117 | line.rsplit_once(' ') 118 | .and_then(|(stack, count)| Some((stack, count.parse().ok()?))) 119 | } 120 | 121 | fn collapse_stack(stack: Cow) -> Cow { 122 | // First, determine whether we can avoid allocation by just returning 123 | // the original stack (in the case that there is no recursion, which is 124 | // likely the mainline case). 125 | if !Self::is_recursive(&stack) { 126 | return stack; 127 | } 128 | 129 | // There is recursion, so we can't get away without allocating a new 130 | // String. 131 | let mut result = String::with_capacity(stack.len()); 132 | let mut last = None; 133 | for frame in stack.split(';') { 134 | if last != Some(frame) { 135 | result.push_str(frame); 136 | result.push(';') 137 | } 138 | last = Some(frame); 139 | } 140 | 141 | // Remove the trailing semicolon 142 | result.pop(); 143 | 144 | result.into() 145 | } 146 | 147 | /// Determine whether or not a stack contains direct recursion. 148 | fn is_recursive(stack: &str) -> bool { 149 | let mut last = None; 150 | for current in stack.split(';') { 151 | match last { 152 | None => { 153 | last = Some(current); 154 | } 155 | Some(l) => { 156 | if l == current { 157 | // Recursion! 158 | return true; 159 | } else { 160 | last = Some(current); 161 | } 162 | } 163 | } 164 | } 165 | false 166 | } 167 | } 168 | 169 | #[cfg(test)] 170 | mod test { 171 | 172 | use super::*; 173 | 174 | #[test] 175 | fn test_collapse_stack() { 176 | assert_eq!(Folder::collapse_stack("".into()), ""); 177 | assert_eq!(Folder::collapse_stack("single".into()), "single"); 178 | assert_eq!( 179 | Folder::collapse_stack("not;recursive".into()), 180 | "not;recursive" 181 | ); 182 | assert_eq!( 183 | Folder::collapse_stack("has;some;some;recursion;recursion".into()), 184 | "has;some;recursion" 185 | ); 186 | assert_eq!( 187 | Folder::collapse_stack("co;recursive;co;recursive".into()), 188 | "co;recursive;co;recursive" 189 | ); 190 | } 191 | 192 | #[test] 193 | fn test_line_parts() { 194 | assert_eq!( 195 | Folder::line_parts("foo;bar;baz 42"), 196 | Some(("foo;bar;baz", 42)) 197 | ); 198 | assert_eq!(Folder::line_parts(""), None); 199 | assert_eq!(Folder::line_parts("no;number"), None); 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /src/collapse/vtune.rs: -------------------------------------------------------------------------------- 1 | use std::io::{self, BufRead}; 2 | 3 | use log::warn; 4 | 5 | use crate::collapse::common::Occurrences; 6 | use crate::collapse::Collapse; 7 | 8 | // The call graph begins after this line. 9 | static HEADER: &str = "Function Stack,CPU Time:Self,Module"; 10 | 11 | /// `vtune` folder configuration options. 12 | #[derive(Clone, Debug, Default)] 13 | #[non_exhaustive] 14 | pub struct Options { 15 | /// Don't include modules with function names. 16 | /// 17 | /// Default is `false`. 18 | pub no_modules: bool, 19 | } 20 | 21 | /// A stack collapser for CSV call graphs created with the VTune `amplxe-cl` tool. 22 | /// 23 | /// To construct one, either use `vtune::Folder::default()` or create an [`Options`] and use 24 | /// `vtune::Folder::from(options)`. 25 | #[derive(Clone, Default)] 26 | pub struct Folder { 27 | /// Function on the stack in this entry thus far. 28 | stack: Vec, 29 | 30 | opt: Options, 31 | } 32 | 33 | impl Collapse for Folder { 34 | fn collapse(&mut self, mut reader: R, writer: W) -> io::Result<()> 35 | where 36 | R: io::BufRead, 37 | W: io::Write, 38 | { 39 | // Consume the header... 40 | let mut line = Vec::new(); 41 | loop { 42 | line.clear(); 43 | if reader.read_until(0x0A, &mut line)? == 0 { 44 | warn!("File ended before header"); 45 | return Ok(()); 46 | }; 47 | let l = String::from_utf8_lossy(&line); 48 | if l.starts_with(HEADER) { 49 | break; 50 | } 51 | } 52 | 53 | // Process the data... 54 | let mut occurrences = Occurrences::new(1); 55 | loop { 56 | line.clear(); 57 | if reader.read_until(0x0A, &mut line)? == 0 { 58 | break; 59 | } 60 | let l = String::from_utf8_lossy(&line); 61 | let line = l.trim_end(); 62 | if line.is_empty() { 63 | continue; 64 | } else { 65 | self.on_line(line, &mut occurrences)?; 66 | } 67 | } 68 | 69 | // Write the results... 70 | occurrences.write_and_clear(writer)?; 71 | 72 | // Reset the state... 73 | self.stack.clear(); 74 | Ok(()) 75 | } 76 | 77 | /// Check for header 78 | fn is_applicable(&mut self, input: &str) -> Option { 79 | let mut input = input.as_bytes(); 80 | let mut line = String::new(); 81 | loop { 82 | line.clear(); 83 | if let Ok(n) = input.read_line(&mut line) { 84 | if n == 0 { 85 | break; 86 | } 87 | } else { 88 | return Some(false); 89 | } 90 | 91 | if line.starts_with(HEADER) { 92 | return Some(true); 93 | } 94 | } 95 | None 96 | } 97 | } 98 | 99 | impl From for Folder { 100 | fn from(opt: Options) -> Self { 101 | Folder { 102 | opt, 103 | ..Default::default() 104 | } 105 | } 106 | } 107 | 108 | impl Folder { 109 | fn line_parts<'a>(&self, line: &'a str) -> Option<(&'a str, &'a str, &'a str)> { 110 | let mut line = if let Some(line) = line.strip_prefix('"') { 111 | // The function name will be in quotes if it contains spaces. 112 | line.splitn(2, "\",") 113 | } else { 114 | // We split on a string because we need to match the type of the other if branch. 115 | #[allow(clippy::single_char_pattern)] 116 | line.splitn(2, ",") 117 | }; 118 | 119 | let func = line.next()?; 120 | let mut line = line.next()?.splitn(2, ','); 121 | let time = line.next()?; 122 | let module = if self.opt.no_modules { 123 | "" 124 | } else { 125 | line.next()? 126 | }; 127 | 128 | Some((func, time, module)) 129 | } 130 | 131 | fn on_line(&mut self, line: &str, occurrences: &mut Occurrences) -> io::Result<()> { 132 | if let Some(spaces) = line.find(|c| c != ' ') { 133 | let prev_depth = self.stack.len(); 134 | let depth = spaces + 1; 135 | 136 | if depth <= prev_depth { 137 | // If the depth of this line is less than the previous one, 138 | // it means the previous line was a leaf node and we should 139 | // pop the stack back to one before the current depth. 140 | for _ in 0..=prev_depth - depth { 141 | self.stack.pop(); 142 | } 143 | } else if depth > prev_depth + 1 { 144 | return invalid_data_error!("Skipped indentation level at line:\n{}", line); 145 | } 146 | 147 | if let Some((func, time, module)) = self.line_parts(&line[spaces..]) { 148 | if let Ok(time) = time.parse::() { 149 | let time_ms = (time * 1000.0).round() as usize; 150 | if module.is_empty() { 151 | self.stack.push(func.to_string()); 152 | } else { 153 | self.stack.push(format!("{}`{}", module, func)); 154 | } 155 | if time_ms > 0 { 156 | self.write_stack(occurrences, time_ms); 157 | } 158 | } else { 159 | return invalid_data_error!("Invalid `CPU Time:Self` field: {}", time); 160 | } 161 | } else { 162 | return invalid_data_error!("Unable to parse stack line:\n{}", line); 163 | } 164 | } 165 | 166 | Ok(()) 167 | } 168 | 169 | fn write_stack(&self, occurrences: &mut Occurrences, time: usize) { 170 | occurrences.insert(self.stack.join(";"), time); 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/differential/mod.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::io::{self, prelude::*}; 3 | use std::path::Path; 4 | 5 | use ahash::AHashMap; 6 | use log::warn; 7 | 8 | const READER_CAPACITY: usize = 128 * 1024; 9 | 10 | #[derive(Debug, Clone, Copy, Default)] 11 | struct Counts { 12 | first: usize, 13 | second: usize, 14 | } 15 | 16 | /// Configure the generated output. 17 | /// 18 | /// All options default to off. 19 | #[derive(Debug, Clone, Copy, Default)] 20 | pub struct Options { 21 | /// Normalize the first profile count to match the second. 22 | /// 23 | /// This can help in scenarios where you take profiles at different times, under varying 24 | /// load. If you generate a differential flame graph without setting this flag, everything 25 | /// will look red if the load increased, or blue if it decreased. If this flag is set, 26 | /// the first profile is balanced so you get the full red/blue spectrum. 27 | pub normalize: bool, 28 | 29 | /// Strip hex numbers (addresses) of the form "0x45ef2173" and replace with "0x...". 30 | pub strip_hex: bool, 31 | } 32 | 33 | /// Produce an output that can be used to generate a differential flame graph. 34 | /// 35 | /// The readers are expected to contain folded stack lines of before and after profiles with 36 | /// the following whitespace-separated fields: 37 | /// 38 | /// - A semicolon-separated list of frame names (e.g., `main;foo;bar;baz`). 39 | /// - A sample count for the given stack. 40 | /// 41 | /// The output written to the `writer` will be similar to the inputs, except there will be two 42 | /// sample count columns -- one for each profile. 43 | pub fn from_readers(opt: Options, before: R1, after: R2, writer: W) -> io::Result<()> 44 | where 45 | R1: BufRead, 46 | R2: BufRead, 47 | W: Write, 48 | { 49 | let mut stack_counts = AHashMap::default(); 50 | let total1 = parse_stack_counts(opt, &mut stack_counts, before, true)?; 51 | let total2 = parse_stack_counts(opt, &mut stack_counts, after, false)?; 52 | if opt.normalize && total1 != total2 { 53 | for counts in stack_counts.values_mut() { 54 | counts.first = (counts.first as f64 * total2 as f64 / total1 as f64) as usize; 55 | } 56 | } 57 | write_stacks(&stack_counts, writer) 58 | } 59 | 60 | /// Produce an output that can be used to generate a differential flame graph from 61 | /// a before and an after profile. 62 | /// 63 | /// See [`from_readers`] for the input and output formats. 64 | pub fn from_files( 65 | opt: Options, 66 | file_before: P1, 67 | file_after: P2, 68 | writer: W, 69 | ) -> io::Result<()> 70 | where 71 | P1: AsRef, 72 | P2: AsRef, 73 | W: Write, 74 | { 75 | let file1 = File::open(file_before)?; 76 | let reader1 = io::BufReader::with_capacity(READER_CAPACITY, file1); 77 | let file2 = File::open(file_after)?; 78 | let reader2 = io::BufReader::with_capacity(READER_CAPACITY, file2); 79 | from_readers(opt, reader1, reader2, writer) 80 | } 81 | 82 | // Populate stack_counts based on lines from the reader and returns the sum of the sample counts. 83 | fn parse_stack_counts( 84 | opt: Options, 85 | stack_counts: &mut AHashMap, 86 | mut reader: R, 87 | is_first: bool, 88 | ) -> io::Result 89 | where 90 | R: BufRead, 91 | { 92 | let mut total = 0; 93 | let mut line = Vec::new(); 94 | let mut stripped_fractional_samples = false; 95 | loop { 96 | line.clear(); 97 | 98 | if reader.read_until(0x0A, &mut line)? == 0 { 99 | break; 100 | } 101 | 102 | let l = String::from_utf8_lossy(&line); 103 | if let Some((stack, count)) = 104 | parse_line(&l, opt.strip_hex, &mut stripped_fractional_samples) 105 | { 106 | let counts = stack_counts.entry(stack).or_default(); 107 | if is_first { 108 | counts.first += count; 109 | } else { 110 | counts.second += count; 111 | } 112 | total += count; 113 | } else { 114 | warn!("Unable to parse line: {}", l); 115 | } 116 | } 117 | 118 | Ok(total) 119 | } 120 | 121 | // Write three-column lines with the folded stack trace and two value columns, 122 | // one for each profile. 123 | fn write_stacks(stack_counts: &AHashMap, mut writer: W) -> io::Result<()> 124 | where 125 | W: Write, 126 | { 127 | for (stack, &Counts { first, second }) in stack_counts { 128 | writeln!(writer, "{} {} {}", stack, first, second)?; 129 | } 130 | Ok(()) 131 | } 132 | 133 | // Parse stack and sample count from line. 134 | fn parse_line( 135 | line: &str, 136 | strip_hex: bool, 137 | stripped_fractional_samples: &mut bool, 138 | ) -> Option<(String, usize)> { 139 | let samplesi = line.rfind(' ')?; 140 | let mut samples = line[samplesi + 1..].trim_end(); 141 | 142 | // Strip fractional part (if any); 143 | // foobar 1.klwdjlakdj 144 | // 145 | // The Perl version keeps the fractional part but inferno 146 | // strips them in its flamegraph implementation anyway. 147 | if let Some(doti) = samples.find('.') { 148 | if !samples[..doti] 149 | .chars() 150 | .chain(samples[doti + 1..].chars()) 151 | .all(|c| c.is_ascii_digit()) 152 | { 153 | return None; 154 | } 155 | // Warn if we're stripping a non-zero fractional part, but only the first time. 156 | if !*stripped_fractional_samples && !samples[doti + 1..].chars().all(|c| c == '0') { 157 | *stripped_fractional_samples = true; 158 | warn!("The input data has fractional sample counts that will be truncated to integers"); 159 | } 160 | samples = &samples[..doti]; 161 | } 162 | 163 | let nsamples = samples.parse::().ok()?; 164 | let stack = line[..samplesi].trim_end(); 165 | if strip_hex { 166 | Some((strip_hex_address(stack), nsamples)) 167 | } else { 168 | Some((stack.to_string(), nsamples)) 169 | } 170 | } 171 | 172 | // Replace all hex strings like "0x45ef2173" with "0x...". 173 | fn strip_hex_address(mut stack: &str) -> String { 174 | let mut stripped = String::with_capacity(stack.len()); 175 | while let Some(idx) = stack.find("0x") { 176 | stripped.push_str(&stack[..idx + 2]); 177 | let ndigits = stack[idx + 2..] 178 | .chars() 179 | .take_while(|c| c.is_ascii_hexdigit()) 180 | .count(); 181 | if ndigits > 0 { 182 | stripped.push_str("..."); 183 | } 184 | stack = &stack[idx + 2 + ndigits..]; 185 | } 186 | stripped.push_str(stack); 187 | stripped 188 | } 189 | -------------------------------------------------------------------------------- /src/flamegraph/flamegraph.css: -------------------------------------------------------------------------------- 1 | #matched { text-anchor:end; } 2 | #search { text-anchor:end; opacity:0.1; cursor:pointer; } 3 | #search:hover, #search.show { opacity:1; } 4 | #subtitle { text-anchor:middle; font-color:rgb(160,160,160); } 5 | #unzoom { cursor:pointer; } 6 | #frames > *:hover { stroke:black; stroke-width:0.5; cursor:pointer; } 7 | .hide { display:none; } 8 | .parent { opacity:0.5; } 9 | -------------------------------------------------------------------------------- /src/flamegraph/rand.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | 3 | pub(super) struct XorShift64 { 4 | a: u64, 5 | } 6 | 7 | impl XorShift64 { 8 | pub(super) fn new(seed: u64) -> XorShift64 { 9 | XorShift64 { a: seed } 10 | } 11 | 12 | pub(super) fn next(&mut self) -> u64 { 13 | let mut x = self.a; 14 | x ^= x << 13; 15 | x ^= x >> 7; 16 | x ^= x << 17; 17 | self.a = x; 18 | x 19 | } 20 | 21 | pub(super) fn next_f64(&mut self) -> f64 { 22 | sample(self.next()) 23 | } 24 | } 25 | 26 | thread_local! { 27 | pub(super) static RNG: RefCell = RefCell::new(XorShift64::new(1234)); 28 | } 29 | 30 | // Copied from `rand` with minor modifications. 31 | fn sample(value: u64) -> f64 { 32 | let fraction_bits = 52; 33 | 34 | // Multiply-based method; 24/53 random bits; [0, 1) interval. 35 | // We use the most significant bits because for simple RNGs 36 | // those are usually more random. 37 | let float_size = std::mem::size_of::() as u32 * 8; 38 | let precision = fraction_bits + 1; 39 | let scale = 1.0 / ((1_u64 << precision) as f64); 40 | 41 | let value = value >> (float_size - precision); 42 | scale * (value as f64) 43 | } 44 | 45 | pub(super) fn thread_rng() -> impl Fn() -> f32 { 46 | || RNG.with(|rng| rng.borrow_mut().next_f64() as f32) 47 | } 48 | 49 | #[test] 50 | fn test_rng() { 51 | const ITERATIONS: usize = 10000; 52 | 53 | let mut rng = XorShift64::new(1234); 54 | let mut sum = rng.next_f64(); 55 | let mut min = sum; 56 | let mut max = sum; 57 | for _ in 0..ITERATIONS - 1 { 58 | let value = rng.next_f64(); 59 | sum += value; 60 | if value < min { 61 | min = value; 62 | } 63 | if value > max { 64 | max = value; 65 | } 66 | } 67 | 68 | let avg = sum / ITERATIONS as f64; 69 | 70 | // Make sure the RNG is uniform. 71 | assert!(min >= 0.000); 72 | assert!(min <= 0.001); 73 | assert!(max <= 1.000); 74 | assert!(max >= 0.999); 75 | assert!(avg >= 0.490); 76 | assert!(avg <= 0.510); 77 | } 78 | -------------------------------------------------------------------------------- /tests/collapse-dtrace.rs: -------------------------------------------------------------------------------- 1 | mod common; 2 | 3 | use std::fs::File; 4 | use std::io::{self, BufReader, Cursor}; 5 | use std::process::{Command, Stdio}; 6 | 7 | use assert_cmd::cargo::CommandCargoExt; 8 | use inferno::collapse::dtrace::{Folder, Options}; 9 | use log::Level; 10 | use pretty_assertions::assert_eq; 11 | use testing_logger::CapturedLog; 12 | 13 | fn test_collapse_dtrace(test_file: &str, expected_file: &str, options: Options) -> io::Result<()> { 14 | for &n in &[1, 2] { 15 | let mut options = options.clone(); 16 | options.nthreads = n; 17 | common::test_collapse(Folder::from(options), test_file, expected_file, false)?; 18 | } 19 | Ok(()) 20 | } 21 | 22 | fn test_collapse_dtrace_logs_with_options(input_file: &str, asserter: F, mut options: Options) 23 | where 24 | F: Fn(&Vec), 25 | { 26 | // We must run log tests in a single thread to play nicely with `testing_logger`. 27 | options.nthreads = 1; 28 | common::test_collapse_logs(Folder::from(options), input_file, asserter); 29 | } 30 | 31 | fn test_collapse_dtrace_logs(input_file: &str, asserter: F) 32 | where 33 | F: Fn(&Vec), 34 | { 35 | test_collapse_dtrace_logs_with_options(input_file, asserter, Options::default()); 36 | } 37 | 38 | #[test] 39 | fn collapse_dtrace_compare_to_upstream() { 40 | let test_file = "./flamegraph/example-dtrace-stacks.txt"; 41 | let result_file = "./tests/data/collapse-dtrace/results/dtrace-example.txt"; 42 | test_collapse_dtrace(test_file, result_file, Options::default()).unwrap() 43 | } 44 | 45 | #[test] 46 | fn collapse_dtrace_compare_to_upstream_with_offsets() { 47 | let test_file = "./flamegraph/example-dtrace-stacks.txt"; 48 | let result_file = "./tests/data/collapse-dtrace/results/dtrace-example-offsets.txt"; 49 | 50 | let mut options = Options::default(); 51 | options.includeoffset = true; 52 | 53 | test_collapse_dtrace(test_file, result_file, options).unwrap() 54 | } 55 | 56 | #[test] 57 | fn collapse_dtrace_compare_to_upstream_java() { 58 | let test_file = "./tests/data/collapse-dtrace/java.txt"; 59 | let result_file = "./tests/data/collapse-dtrace/results/java.txt"; 60 | test_collapse_dtrace(test_file, result_file, Options::default()).unwrap() 61 | } 62 | 63 | #[test] 64 | fn collapse_dtrace_hex_addresses() { 65 | let test_file = "./tests/data/collapse-dtrace/hex-addresses.txt"; 66 | let result_file = "./tests/data/collapse-dtrace/results/hex-addresses.txt"; 67 | test_collapse_dtrace(test_file, result_file, Options::default()).unwrap() 68 | } 69 | 70 | #[test] 71 | fn collapse_dtrace_compare_to_flamegraph_bug() { 72 | // There is a bug in flamegraph that causes the following stack to render 73 | // badly. We fix this but keep the test around to point out this breakage 74 | // of bug compatibility. 75 | // 76 | // https://github.com/brendangregg/FlameGraph/issues/202 77 | let test_file = "./tests/data/collapse-dtrace/flamegraph-bug.txt"; 78 | let result_file = "./tests/data/collapse-dtrace/results/flamegraph-bug.txt"; 79 | 80 | let mut options = Options::default(); 81 | options.includeoffset = true; 82 | 83 | test_collapse_dtrace(test_file, result_file, options).unwrap() 84 | } 85 | 86 | #[test] 87 | fn collapse_dtrace_stack_ustack() { 88 | let test_file = "./tests/data/collapse-dtrace/stack-ustack.txt"; 89 | let result_file = "./tests/data/collapse-dtrace/results/stack-ustack.txt"; 90 | 91 | test_collapse_dtrace(test_file, result_file, Options::default()).unwrap() 92 | } 93 | 94 | #[test] 95 | fn collapse_dtrace_should_log_warning_for_only_header_lines() { 96 | test_collapse_dtrace_logs( 97 | "./tests/data/collapse-dtrace/only-header-lines.txt", 98 | |captured_logs| { 99 | let nwarnings = captured_logs 100 | .iter() 101 | .filter(|log| { 102 | log.body == "File ended while skipping headers" && log.level == Level::Warn 103 | }) 104 | .count(); 105 | assert_eq!( 106 | nwarnings, 1, 107 | "warning logged {} times, but should be logged exactly once", 108 | nwarnings 109 | ); 110 | }, 111 | ); 112 | } 113 | 114 | #[test] 115 | fn collapse_dtrace_scope_with_no_argument_list() { 116 | let test_file = "./tests/data/collapse-dtrace/scope_with_no_argument_list.txt"; 117 | let result_file = "./tests/data/collapse-dtrace/results/scope_with_no_argument_list.txt"; 118 | test_collapse_dtrace(test_file, result_file, Options::default()).unwrap() 119 | } 120 | 121 | #[test] 122 | fn collapse_dtrace_rust_names() { 123 | let test_file = "./tests/data/collapse-dtrace/rust-names.txt"; 124 | let result_file = "./tests/data/collapse-dtrace/results/rust-names.txt"; 125 | test_collapse_dtrace(test_file, result_file, Options::default()).unwrap() 126 | } 127 | 128 | #[test] 129 | fn collapse_dtrace_cli() { 130 | let input_file = "./flamegraph/example-dtrace-stacks.txt"; 131 | let expected_file = "./tests/data/collapse-dtrace/results/dtrace-example.txt"; 132 | 133 | // Test with file passed in 134 | let output = Command::cargo_bin("inferno-collapse-dtrace") 135 | .unwrap() 136 | .arg(input_file) 137 | .output() 138 | .expect("failed to execute process"); 139 | let expected = BufReader::new(File::open(expected_file).unwrap()); 140 | common::compare_results(Cursor::new(output.stdout), expected, expected_file, false); 141 | 142 | // Test with STDIN 143 | let mut child = Command::cargo_bin("inferno-collapse-dtrace") 144 | .unwrap() 145 | .stdin(Stdio::piped()) 146 | .stdout(Stdio::piped()) 147 | .spawn() 148 | .expect("Failed to spawn child process"); 149 | let mut input = BufReader::new(File::open(input_file).unwrap()); 150 | let stdin = child.stdin.as_mut().expect("Failed to open stdin"); 151 | io::copy(&mut input, stdin).unwrap(); 152 | let output = child.wait_with_output().expect("Failed to read stdout"); 153 | let expected = BufReader::new(File::open(expected_file).unwrap()); 154 | common::compare_results(Cursor::new(output.stdout), expected, expected_file, false); 155 | } 156 | -------------------------------------------------------------------------------- /tests/collapse-ghcprof.rs: -------------------------------------------------------------------------------- 1 | mod common; 2 | 3 | use std::fs::File; 4 | use std::io::{self, BufReader, Cursor}; 5 | use std::process::{Command, Stdio}; 6 | 7 | use assert_cmd::prelude::CommandCargoExt; 8 | use inferno::collapse::ghcprof::{Folder, Options, Source}; 9 | 10 | fn test_collapse_ghcprof(test_file: &str, expected_file: &str, options: Options) -> io::Result<()> { 11 | common::test_collapse(Folder::from(options), test_file, expected_file, false) 12 | } 13 | 14 | #[test] 15 | fn collapse_percent_default() { 16 | let test_file = "./tests/data/collapse-ghcprof/percent.prof"; 17 | let result_file = "./tests/data/collapse-ghcprof/results/percent.txt"; 18 | test_collapse_ghcprof(test_file, result_file, Options::default()).unwrap() 19 | } 20 | 21 | #[test] 22 | fn collapse_ticks_default() { 23 | let test_file = "./tests/data/collapse-ghcprof/ticks.prof"; 24 | let result_file = "./tests/data/collapse-ghcprof/results/ticks.txt"; 25 | test_collapse_ghcprof(test_file, result_file, Options::default()).unwrap() 26 | } 27 | 28 | #[test] 29 | fn collapse_ticks_percent() { 30 | let test_file = "./tests/data/collapse-ghcprof/ticks.prof"; 31 | let result_file = "./tests/data/collapse-ghcprof/results/ticks.txt"; 32 | let mut options = Options::default(); 33 | options.source = Source::PercentTime; 34 | test_collapse_ghcprof(test_file, result_file, options).unwrap() 35 | } 36 | 37 | #[test] 38 | fn collapse_ticks_ticks() { 39 | let test_file = "./tests/data/collapse-ghcprof/ticks.prof"; 40 | let result_file = "./tests/data/collapse-ghcprof/results/ticks_ticks.txt"; 41 | let mut options = Options::default(); 42 | options.source = Source::Ticks; 43 | test_collapse_ghcprof(test_file, result_file, options).unwrap() 44 | } 45 | 46 | #[test] 47 | fn collapse_bytes_bytes() { 48 | let test_file = "./tests/data/collapse-ghcprof/ticks.prof"; 49 | let result_file = "./tests/data/collapse-ghcprof/results/ticks_bytes.txt"; 50 | let mut options = Options::default(); 51 | options.source = Source::Bytes; 52 | test_collapse_ghcprof(test_file, result_file, options).unwrap() 53 | } 54 | 55 | #[test] 56 | fn collapse_utf8_default() { 57 | let test_file = "./tests/data/collapse-ghcprof/utf8.prof"; 58 | let result_file = "./tests/data/collapse-ghcprof/results/utf8.txt"; 59 | test_collapse_ghcprof(test_file, result_file, Options::default()).unwrap() 60 | } 61 | 62 | #[test] 63 | fn collapse_utf8_ticks() { 64 | let test_file = "./tests/data/collapse-ghcprof/utf8.prof"; 65 | let result_file = "./tests/data/collapse-ghcprof/results/utf8_ticks.txt"; 66 | let mut options = Options::default(); 67 | options.source = Source::Ticks; 68 | test_collapse_ghcprof(test_file, result_file, options).unwrap() 69 | } 70 | 71 | #[test] 72 | fn collapse_utf8_bytes() { 73 | let test_file = "./tests/data/collapse-ghcprof/utf8.prof"; 74 | let result_file = "./tests/data/collapse-ghcprof/results/utf8_bytes.txt"; 75 | let mut options = Options::default(); 76 | options.source = Source::Bytes; 77 | test_collapse_ghcprof(test_file, result_file, options).unwrap() 78 | } 79 | 80 | #[test] 81 | fn collapse_ghcprof_cli() { 82 | let input_file = "./tests/data/collapse-ghcprof/ticks.prof"; 83 | let expected_file = "./tests/data/collapse-ghcprof/results/ticks.txt"; 84 | 85 | // Test with file passed in 86 | let output = Command::cargo_bin("inferno-collapse-ghcprof") 87 | .unwrap() 88 | .arg(input_file) 89 | .output() 90 | .expect("failed to execute process"); 91 | let expected = BufReader::new(File::open(expected_file).unwrap()); 92 | common::compare_results(Cursor::new(output.stdout), expected, expected_file, false); 93 | 94 | // Test with STDIN 95 | let mut child = Command::cargo_bin("inferno-collapse-ghcprof") 96 | .unwrap() 97 | .stdin(Stdio::piped()) 98 | .stdout(Stdio::piped()) 99 | .spawn() 100 | .expect("Failed to spawn child process"); 101 | let mut input = BufReader::new(File::open(input_file).unwrap()); 102 | let stdin = child.stdin.as_mut().expect("Failed to open stdin"); 103 | io::copy(&mut input, stdin).unwrap(); 104 | let output = child.wait_with_output().expect("Failed to read stdout"); 105 | let expected = BufReader::new(File::open(expected_file).unwrap()); 106 | common::compare_results(Cursor::new(output.stdout), expected, expected_file, false); 107 | 108 | // Test --ticks and --bytes conflict 109 | let output = Command::cargo_bin("inferno-collapse-ghcprof") 110 | .unwrap() 111 | .arg(input_file) 112 | .arg("--ticks") 113 | .arg("--bytes") 114 | .output() 115 | .expect("failed to execute process"); 116 | assert!(!output.status.success()); 117 | } 118 | -------------------------------------------------------------------------------- /tests/collapse-recursive.rs: -------------------------------------------------------------------------------- 1 | mod common; 2 | 3 | use std::fs::File; 4 | use std::io::{self, BufReader, Cursor}; 5 | use std::process::{Command, Stdio}; 6 | 7 | use assert_cmd::cargo::CommandCargoExt; 8 | use inferno::collapse::recursive::{Folder, Options}; 9 | 10 | fn test_collapse_recursive( 11 | test_file: &str, 12 | expected_file: &str, 13 | options: Options, 14 | ) -> io::Result<()> { 15 | for &n in &[1, 2] { 16 | let mut options = options.clone(); 17 | options.nthreads = n; 18 | common::test_collapse(Folder::from(options), test_file, expected_file, false)?; 19 | } 20 | Ok(()) 21 | } 22 | 23 | #[test] 24 | fn collapse_recursive_basic() { 25 | let test_file = "./tests/data/collapse-recursive/basic.txt"; 26 | let result_file = "./tests/data/collapse-recursive/results/basic-collapsed.txt"; 27 | test_collapse_recursive(test_file, result_file, Options::default()).unwrap() 28 | } 29 | 30 | #[test] 31 | fn collapse_recursive_cli() { 32 | let input_file = "./tests/data/collapse-recursive/basic.txt"; 33 | let expected_file = "./tests/data/collapse-recursive/results/basic-collapsed.txt"; 34 | 35 | // Test with file passed in 36 | let output = Command::cargo_bin("inferno-collapse-recursive") 37 | .unwrap() 38 | .arg(input_file) 39 | .output() 40 | .expect("failed to execute process"); 41 | let expected = BufReader::new(File::open(expected_file).unwrap()); 42 | common::compare_results(Cursor::new(output.stdout), expected, expected_file, false); 43 | 44 | // Test with STDIN 45 | let mut child = Command::cargo_bin("inferno-collapse-recursive") 46 | .unwrap() 47 | .stdin(Stdio::piped()) 48 | .stdout(Stdio::piped()) 49 | .spawn() 50 | .expect("Failed to spawn child process"); 51 | let mut input = BufReader::new(File::open(input_file).unwrap()); 52 | let stdin = child.stdin.as_mut().expect("Failed to open stdin"); 53 | io::copy(&mut input, stdin).unwrap(); 54 | let output = child.wait_with_output().expect("Failed to read stdout"); 55 | let expected = BufReader::new(File::open(expected_file).unwrap()); 56 | common::compare_results(Cursor::new(output.stdout), expected, expected_file, false); 57 | } 58 | -------------------------------------------------------------------------------- /tests/collapse-vsprof.rs: -------------------------------------------------------------------------------- 1 | mod common; 2 | 3 | use std::fs::File; 4 | use std::io::{self, BufReader, Cursor}; 5 | use std::process::{Command, Stdio}; 6 | 7 | use assert_cmd::prelude::*; 8 | use inferno::collapse::vsprof::Folder; 9 | use log::Level; 10 | use pretty_assertions::assert_eq; 11 | use testing_logger::CapturedLog; 12 | 13 | fn test_collapse_vsprof(test_file: &str, expected_file: &str) -> io::Result<()> { 14 | common::test_collapse(Folder::default(), test_file, expected_file, false) 15 | } 16 | 17 | fn test_collapse_vsprof_error(test_file: &str) -> io::Error { 18 | common::test_collapse_error(Folder::default(), test_file) 19 | } 20 | 21 | fn test_collapse_vsprof_logs(input_file: &str, asserter: F) 22 | where 23 | F: Fn(&Vec), 24 | { 25 | common::test_collapse_logs(Folder::default(), input_file, asserter); 26 | } 27 | 28 | #[test] 29 | fn collapse_vsprof_default() { 30 | let test_file = "./tests/data/collapse-vsprof/CallTreeSummary.csv"; 31 | let result_file = "./tests/data/collapse-vsprof/results/sample-default.txt"; 32 | test_collapse_vsprof(test_file, result_file).unwrap() 33 | } 34 | 35 | #[test] 36 | fn collapse_vsprof_should_log_warning_for_ending_before_call_graph_start() { 37 | test_collapse_vsprof_logs( 38 | "./tests/data/collapse-vsprof/empty-file.csv", 39 | |captured_logs| { 40 | let nwarnings = captured_logs 41 | .iter() 42 | .filter(|log| { 43 | log.body == "File ended before start of call graph" && log.level == Level::Warn 44 | }) 45 | .count(); 46 | assert_eq!( 47 | nwarnings, 1, 48 | "warning logged {} times, but should be logged exactly once", 49 | nwarnings 50 | ); 51 | }, 52 | ); 53 | } 54 | 55 | #[test] 56 | fn collapse_vsprof_should_return_error_for_incorrect_header() { 57 | let test_file = "./tests/data/collapse-vsprof/incorrect-header.csv"; 58 | let error = test_collapse_vsprof_error(test_file); 59 | assert_eq!(error.kind(), io::ErrorKind::InvalidData); 60 | assert!(error 61 | .to_string() 62 | .starts_with("Expected first line to be header line")); 63 | } 64 | 65 | #[test] 66 | fn collapse_vsprof_should_return_error_for_missing_function_name() { 67 | let test_file = "./tests/data/collapse-vsprof/missing-function-name.csv"; 68 | let error = test_collapse_vsprof_error(test_file); 69 | assert_eq!(error.kind(), io::ErrorKind::InvalidData); 70 | assert!(error 71 | .to_string() 72 | .starts_with("Missing function name in line:")); 73 | } 74 | 75 | #[test] 76 | fn collapse_vsprof_should_return_error_for_invalid_function_name() { 77 | let test_file = "./tests/data/collapse-vsprof/invalid-function-name.csv"; 78 | let error = test_collapse_vsprof_error(test_file); 79 | assert_eq!(error.kind(), io::ErrorKind::InvalidData); 80 | assert!(error 81 | .to_string() 82 | .starts_with("Unable to parse function name from line:")); 83 | } 84 | 85 | #[test] 86 | fn collapse_vsprof_should_return_error_for_invalid_depth() { 87 | let test_file = "./tests/data/collapse-vsprof/invalid-depth.csv"; 88 | let error = test_collapse_vsprof_error(test_file); 89 | assert_eq!(error.kind(), io::ErrorKind::InvalidData); 90 | assert!(error 91 | .to_string() 92 | .starts_with("Unable to parse integer from")); 93 | } 94 | 95 | #[test] 96 | fn collapse_vsprof_should_return_error_for_invalid_number_of_calls() { 97 | let test_file = "./tests/data/collapse-vsprof/invalid-number-of-calls.csv"; 98 | let error = test_collapse_vsprof_error(test_file); 99 | assert_eq!(error.kind(), io::ErrorKind::InvalidData); 100 | assert!(error 101 | .to_string() 102 | .starts_with("Unable to parse integer from")); 103 | } 104 | 105 | #[test] 106 | fn collapse_vsprof_cli() { 107 | let input_file = "./tests/data/collapse-vsprof/CallTreeSummary.csv"; 108 | let expected_file = "./tests/data/collapse-vsprof/results/sample-default.txt"; 109 | 110 | // Test with file passed in 111 | let output = Command::cargo_bin("inferno-collapse-vsprof") 112 | .unwrap() 113 | .arg(input_file) 114 | .output() 115 | .expect("failed to execute process"); 116 | let expected = BufReader::new(File::open(expected_file).unwrap()); 117 | common::compare_results(Cursor::new(output.stdout), expected, expected_file, false); 118 | 119 | // Test with STDIN 120 | let mut child = Command::cargo_bin("inferno-collapse-vsprof") 121 | .unwrap() 122 | .stdin(Stdio::piped()) 123 | .stdout(Stdio::piped()) 124 | .spawn() 125 | .expect("Failed to spawn child process"); 126 | let mut input = BufReader::new(File::open(input_file).unwrap()); 127 | let stdin = child.stdin.as_mut().expect("Failed to open stdin"); 128 | io::copy(&mut input, stdin).unwrap(); 129 | let output = child.wait_with_output().expect("Failed to read stdout"); 130 | let expected = BufReader::new(File::open(expected_file).unwrap()); 131 | common::compare_results(Cursor::new(output.stdout), expected, expected_file, false); 132 | } 133 | -------------------------------------------------------------------------------- /tests/collapse-vtune.rs: -------------------------------------------------------------------------------- 1 | mod common; 2 | 3 | use std::fs::File; 4 | use std::io::{self, BufReader, Cursor}; 5 | use std::process::{Command, Stdio}; 6 | 7 | use assert_cmd::prelude::*; 8 | use inferno::collapse::vtune::{Folder, Options}; 9 | use log::Level; 10 | use pretty_assertions::assert_eq; 11 | use testing_logger::CapturedLog; 12 | 13 | fn test_collapse_vtune(test_file: &str, expected_file: &str, options: Options) -> io::Result<()> { 14 | common::test_collapse(Folder::from(options), test_file, expected_file, false) 15 | } 16 | 17 | fn test_collapse_vtune_error(test_file: &str, options: Options) -> io::Error { 18 | common::test_collapse_error(Folder::from(options), test_file) 19 | } 20 | 21 | fn test_collapse_vtune_logs_with_options(input_file: &str, asserter: F, options: Options) 22 | where 23 | F: Fn(&Vec), 24 | { 25 | common::test_collapse_logs(Folder::from(options), input_file, asserter); 26 | } 27 | 28 | fn test_collapse_vtune_logs(input_file: &str, asserter: F) 29 | where 30 | F: Fn(&Vec), 31 | { 32 | test_collapse_vtune_logs_with_options(input_file, asserter, Options::default()); 33 | } 34 | 35 | #[test] 36 | fn collapse_vtune_default() { 37 | let test_file = "./tests/data/collapse-vtune/vtune.csv"; 38 | let result_file = "./tests/data/collapse-vtune/results/vtune-default.txt"; 39 | test_collapse_vtune(test_file, result_file, Options::default()).unwrap() 40 | } 41 | 42 | #[test] 43 | fn collapse_vtune_no_modules() { 44 | let test_file = "./tests/data/collapse-vtune/vtune.csv"; 45 | let result_file = "./tests/data/collapse-vtune/results/vtune-no-modules.txt"; 46 | 47 | let mut options = Options::default(); 48 | options.no_modules = true; 49 | 50 | test_collapse_vtune(test_file, result_file, options).unwrap() 51 | } 52 | 53 | #[test] 54 | fn collapse_vtune_should_log_warning_for_ending_before_header() { 55 | test_collapse_vtune_logs( 56 | "./tests/data/collapse-vtune/end-before-header.csv", 57 | |captured_logs| { 58 | let nwarnings = captured_logs 59 | .iter() 60 | .filter(|log| log.body == "File ended before header" && log.level == Level::Warn) 61 | .count(); 62 | assert_eq!( 63 | nwarnings, 1, 64 | "warning logged {} times, but should be logged exactly once", 65 | nwarnings 66 | ); 67 | }, 68 | ); 69 | } 70 | 71 | #[test] 72 | fn collapse_vtune_should_return_error_for_skipped_indent_level() { 73 | let test_file = "./tests/data/collapse-vtune/skipped-indentation.csv"; 74 | let error = test_collapse_vtune_error(test_file, Options::default()); 75 | assert_eq!(error.kind(), io::ErrorKind::InvalidData); 76 | assert!(error 77 | .to_string() 78 | .starts_with("Skipped indentation level at line")); 79 | } 80 | 81 | #[test] 82 | fn collapse_vtune_should_return_error_for_invalid_time_field() { 83 | let test_file = "./tests/data/collapse-vtune/invalid-time-field.csv"; 84 | let error = test_collapse_vtune_error(test_file, Options::default()); 85 | assert_eq!(error.kind(), io::ErrorKind::InvalidData); 86 | assert!(error 87 | .to_string() 88 | .starts_with("Invalid `CPU Time:Self` field")); 89 | } 90 | 91 | #[test] 92 | fn collapse_vtune_should_return_error_for_bad_stack_line() { 93 | let test_file = "./tests/data/collapse-vtune/bad-stack-line.csv"; 94 | let error = test_collapse_vtune_error(test_file, Options::default()); 95 | assert_eq!(error.kind(), io::ErrorKind::InvalidData); 96 | assert!(error.to_string().starts_with("Unable to parse stack line")); 97 | } 98 | 99 | #[test] 100 | fn collapse_vtune_cli() { 101 | let input_file = "./tests/data/collapse-vtune/vtune.csv"; 102 | let expected_file = "./tests/data/collapse-vtune/results/vtune-default.txt"; 103 | 104 | // Test with file passed in 105 | let output = Command::cargo_bin("inferno-collapse-vtune") 106 | .unwrap() 107 | .arg(input_file) 108 | .output() 109 | .expect("failed to execute process"); 110 | let expected = BufReader::new(File::open(expected_file).unwrap()); 111 | common::compare_results(Cursor::new(output.stdout), expected, expected_file, false); 112 | 113 | // Test with STDIN 114 | let mut child = Command::cargo_bin("inferno-collapse-vtune") 115 | .unwrap() 116 | .stdin(Stdio::piped()) 117 | .stdout(Stdio::piped()) 118 | .spawn() 119 | .expect("Failed to spawn child process"); 120 | let mut input = BufReader::new(File::open(input_file).unwrap()); 121 | let stdin = child.stdin.as_mut().expect("Failed to open stdin"); 122 | io::copy(&mut input, stdin).unwrap(); 123 | let output = child.wait_with_output().expect("Failed to read stdout"); 124 | let expected = BufReader::new(File::open(expected_file).unwrap()); 125 | common::compare_results(Cursor::new(output.stdout), expected, expected_file, false); 126 | } 127 | -------------------------------------------------------------------------------- /tests/collapse-xctrace.rs: -------------------------------------------------------------------------------- 1 | mod common; 2 | 3 | use std::fs::File; 4 | use std::io::{self, BufReader, Cursor}; 5 | use std::process::{Command, Stdio}; 6 | 7 | use assert_cmd::cargo::CommandCargoExt; 8 | use inferno::collapse::xctrace::Folder; 9 | 10 | fn test_collapse_xctrace(test_file: &str, expected_file: &str) -> io::Result<()> { 11 | common::test_collapse(Folder::default(), test_file, expected_file, false)?; 12 | Ok(()) 13 | } 14 | 15 | #[test] 16 | fn collapse_xctrace_basic() { 17 | let test_file = "./tests/data/collapse-xctrace/basic.xml"; 18 | let result_file = "./tests/data/collapse-xctrace/results/basic.folded"; 19 | test_collapse_xctrace(test_file, result_file).unwrap() 20 | } 21 | 22 | #[test] 23 | fn collapse_xctrace_simple_frame_without_binary_info() { 24 | let test_file = "./tests/data/collapse-xctrace/simple_frame_without_binary_info.xml"; 25 | let result_file = 26 | "./tests/data/collapse-xctrace/results/simple_frame_without_binary_info.folded"; 27 | test_collapse_xctrace(test_file, result_file).unwrap() 28 | } 29 | 30 | #[test] 31 | fn collapse_xctrace_cli() { 32 | let input_file = "./tests/data/collapse-xctrace/basic.xml"; 33 | let expected_file = "./tests/data/collapse-xctrace/results/basic.folded"; 34 | 35 | // Test with file passed in 36 | let output = Command::cargo_bin("inferno-collapse-xctrace") 37 | .unwrap() 38 | .arg(input_file) 39 | .output() 40 | .expect("failed to execute process"); 41 | let expected = BufReader::new(File::open(expected_file).unwrap()); 42 | common::compare_results(Cursor::new(output.stdout), expected, expected_file, false); 43 | 44 | // Test with STDIN 45 | let mut child = Command::cargo_bin("inferno-collapse-xctrace") 46 | .unwrap() 47 | .stdin(Stdio::piped()) 48 | .stdout(Stdio::piped()) 49 | .spawn() 50 | .expect("Failed to spawn child process"); 51 | let mut input = BufReader::new(File::open(input_file).unwrap()); 52 | let stdin = child.stdin.as_mut().expect("Failed to open stdin"); 53 | io::copy(&mut input, stdin).unwrap(); 54 | let output = child.wait_with_output().expect("Failed to read stdout"); 55 | let expected = BufReader::new(File::open(expected_file).unwrap()); 56 | common::compare_results(Cursor::new(output.stdout), expected, expected_file, false); 57 | } 58 | -------------------------------------------------------------------------------- /tests/common/collapse.rs: -------------------------------------------------------------------------------- 1 | use std::fs::{self, File}; 2 | use std::io::{self, BufRead, BufReader, Cursor}; 3 | 4 | use inferno::collapse::Collapse; 5 | use libflate::gzip::Decoder; 6 | use pretty_assertions::assert_eq; 7 | use testing_logger::CapturedLog; 8 | 9 | pub fn compare_results(result: R, mut expected: E, expected_file: &str, strip_quotes: bool) 10 | where 11 | R: BufRead, 12 | E: BufRead, 13 | { 14 | let mut buf = String::new(); 15 | let mut line_num = 1; 16 | for line in result.lines() { 17 | let line = if strip_quotes { 18 | line.unwrap().replace(['\"', '\''], "") 19 | } else { 20 | line.unwrap() 21 | }; 22 | if expected.read_line(&mut buf).unwrap() == 0 { 23 | panic!( 24 | "\noutput has more lines than expected result file: {}", 25 | expected_file 26 | ); 27 | } 28 | assert_eq!(line, buf.trim_end(), "\n{}:{}", expected_file, line_num); 29 | buf.clear(); 30 | line_num += 1; 31 | } 32 | 33 | if expected.read_line(&mut buf).unwrap() > 0 { 34 | panic!( 35 | "\n{} has more lines than output, beginning at line: {}", 36 | expected_file, line_num 37 | ) 38 | } 39 | } 40 | 41 | pub fn test_collapse( 42 | mut collapser: C, 43 | test_filename: &str, 44 | expected_filename: &str, 45 | strip_quotes: bool, 46 | ) -> io::Result<()> 47 | where 48 | C: Collapse, 49 | { 50 | if let Err(e) = fs::metadata(test_filename) { 51 | eprintln!("Failed to open input file '{}'", test_filename); 52 | return Err(e); 53 | } 54 | 55 | let mut collapse = move |out: &mut dyn io::Write| { 56 | if test_filename.ends_with(".gz") { 57 | let test_file = File::open(test_filename)?; 58 | let r = BufReader::new(Decoder::new(test_file).unwrap()); 59 | collapser.collapse(r, out) 60 | } else { 61 | collapser.collapse_file(Some(test_filename), out) 62 | } 63 | }; 64 | 65 | let metadata = match fs::metadata(expected_filename) { 66 | Ok(m) => m, 67 | Err(e) => { 68 | if e.kind() == io::ErrorKind::NotFound { 69 | // be nice to the dev and make the file 70 | let mut f = File::create(expected_filename).unwrap(); 71 | collapse(&mut f)?; 72 | fs::metadata(expected_filename).unwrap() 73 | } else { 74 | eprintln!("Tried to open {}.", expected_filename); 75 | return Err(e); 76 | } 77 | } 78 | }; 79 | 80 | let expected_len = metadata.len() as usize; 81 | let mut result = Cursor::new(Vec::with_capacity(expected_len)); 82 | collapse(&mut result)?; 83 | let expected = BufReader::new(File::open(expected_filename)?); 84 | // write out the expected result to /tmp for easy restoration 85 | result.set_position(0); 86 | let rand: u64 = rand::random(); 87 | let tm = std::env::temp_dir().join(format!("test-{}.folded", rand)); 88 | if fs::write(&tm, result.get_ref()).is_ok() { 89 | eprintln!("test output in {}", tm.display()); 90 | } 91 | // and then compare 92 | if let Err(e) = std::panic::catch_unwind(|| { 93 | compare_results(result.clone(), expected, expected_filename, strip_quotes) 94 | }) { 95 | if std::env::var("INFERNO_BLESS_TESTS").is_ok() { 96 | fs::write(expected_filename, result.get_ref()).unwrap(); 97 | } else { 98 | std::panic::resume_unwind(e); 99 | } 100 | } 101 | Ok(()) 102 | } 103 | 104 | pub fn test_collapse_logs(mut collapser: C, input_file: &str, asserter: F) 105 | where 106 | C: Collapse, 107 | F: Fn(&Vec), 108 | { 109 | testing_logger::setup(); 110 | let r = BufReader::new(File::open(input_file).unwrap()); 111 | collapser.collapse(r, std::io::sink()).unwrap(); 112 | testing_logger::validate(asserter); 113 | } 114 | 115 | pub fn test_collapse_error(mut collapser: C, test_filename: &str) -> io::Error 116 | where 117 | C: Collapse, 118 | { 119 | if fs::metadata(test_filename).is_err() { 120 | panic!("Failed to open input file '{}'", test_filename); 121 | } 122 | 123 | let mut collapse = move |out: &mut dyn io::Write| { 124 | if test_filename.ends_with(".gz") { 125 | let test_file = File::open(test_filename)?; 126 | let r = BufReader::new(Decoder::new(test_file).unwrap()); 127 | collapser.collapse(r, out) 128 | } else { 129 | collapser.collapse_file(Some(test_filename), out) 130 | } 131 | }; 132 | 133 | collapse(&mut io::sink()).expect_err("Expected an error") 134 | } 135 | -------------------------------------------------------------------------------- /tests/common/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | #![allow(unused_imports)] 3 | 4 | mod collapse; 5 | 6 | pub use self::collapse::{compare_results, test_collapse, test_collapse_error, test_collapse_logs}; 7 | -------------------------------------------------------------------------------- /tests/data/collapse-dtrace/flamegraph-bug.txt: -------------------------------------------------------------------------------- 1 | CPU ID FUNCTION:NAME 2 | 0 64091 :tick-60s 3 | 4 | 5 | genunix`segvn_fault 6 | unix`pagefault+0x96 7 | unix`trap+0x2c7 8 | unix`0xfffffffffb8001d6 9 | 1 10 | -------------------------------------------------------------------------------- /tests/data/collapse-dtrace/hex-addresses.txt: -------------------------------------------------------------------------------- 1 | CPU ID FUNCTION:NAME 2 | 8 35077 :tick-60s 3 | 4 | 0x104d08831 5 | 0x104cecffd 6 | 0x104cecffd 7 | 0x104cecffd 8 | 0x104ce54e7 9 | libjvm.dylib`JavaCalls::call_helper(JavaValue*, methodHandle*, JavaCallArguments*, Thread*)+0x6ae 10 | libjvm.dylib`JavaCalls::call_virtual(JavaValue*, KlassHandle, Symbol*, Symbol*, JavaCallArguments*, Thread*)+0x164 11 | libjvm.dylib`JavaCalls::call_virtual(JavaValue*, Handle, KlassHandle, Symbol*, Symbol*, Thread*)+0x4a 12 | libjvm.dylib`thread_entry(JavaThread*, Thread*)+0x7c 13 | libjvm.dylib`JavaThread::thread_main_inner()+0x9b 14 | libjvm.dylib`JavaThread::run()+0x1c0 15 | libjvm.dylib`java_start(Thread*)+0xf6 16 | libsystem_pthread.dylib`_pthread_body+0x7e 17 | libsystem_pthread.dylib`_pthread_start+0x46 18 | libsystem_pthread.dylib`thread_start+0xd 19 | 1 20 | 21 | libjvm.dylib`Node::replace_edge(Node*, Node*)+0x4f 22 | libjvm.dylib`PhaseIterGVN::remove_globally_dead_node(Node*)+0x109 23 | libjvm.dylib`PhaseIterGVN::subsume_node(Node*, Node*)+0x199 24 | libjvm.dylib`RegionNode::Ideal(PhaseGVN*, bool)+0x5b3 25 | libjvm.dylib`PhaseIterGVN::transform_old(Node*)+0x3a 26 | libjvm.dylib`PhaseIterGVN::optimize()+0x35 27 | libjvm.dylib`PhaseIdealLoop::build_and_optimize(bool, bool)+0x988 28 | libjvm.dylib`Compile::Optimize()+0x6a5 29 | libjvm.dylib`Compile::Compile(ciEnv*, C2Compiler*, ciMethod*, int, bool, bool, bool)+0x8e1 30 | libjvm.dylib`C2Compiler::compile_method(ciEnv*, ciMethod*, int)+0xb2 31 | libjvm.dylib`CompileBroker::invoke_compiler_on_method(CompileTask*)+0x5b2 32 | libjvm.dylib`CompileBroker::compiler_thread_loop()+0x291 33 | libjvm.dylib`JavaThread::thread_main_inner()+0x9b 34 | libjvm.dylib`JavaThread::run()+0x1c0 35 | libjvm.dylib`java_start(Thread*)+0xf6 36 | libsystem_pthread.dylib`_pthread_body+0x7e 37 | libsystem_pthread.dylib`_pthread_start+0x46 38 | libsystem_pthread.dylib`thread_start+0xd 39 | 1 40 | 41 | libsystem_kernel.dylib`read+0xa 42 | libzip.dylib`ZIP_Read+0xd3 43 | libzip.dylib`Java_java_util_zip_ZipFile_read+0x60 44 | 0x104e7945e 45 | 0x105048088 46 | 0x5a3ef800758e 47 | 2 48 | -------------------------------------------------------------------------------- /tests/data/collapse-dtrace/java.txt: -------------------------------------------------------------------------------- 1 | header 2 | header2 3 | 4 | 5 | test`baz 6 | test`foo->bar 7 | test`main 8 | 1 9 | -------------------------------------------------------------------------------- /tests/data/collapse-dtrace/only-header-lines.txt: -------------------------------------------------------------------------------- 1 | CPU ID FUNCTION:NAME 2 | 8 35077 :tick-60s 3 | -------------------------------------------------------------------------------- /tests/data/collapse-dtrace/results/flamegraph-bug.txt: -------------------------------------------------------------------------------- 1 | unix`0xfffffffffb8001d6;unix`trap+0x2c7;unix`pagefault+0x96;genunix`segvn_fault 1 2 | -------------------------------------------------------------------------------- /tests/data/collapse-dtrace/results/hex-addresses.txt: -------------------------------------------------------------------------------- 1 | 0x5a3ef800758e;0x105048088;0x104e7945e;libzip.dylib`Java_java_util_zip_ZipFile_read;libzip.dylib`ZIP_Read;libsystem_kernel.dylib`read 2 2 | libsystem_pthread.dylib`thread_start;libsystem_pthread.dylib`_pthread_start;libsystem_pthread.dylib`_pthread_body;libjvm.dylib`java_start(Thread*);libjvm.dylib`JavaThread::run;libjvm.dylib`JavaThread::thread_main_inner;libjvm.dylib`CompileBroker::compiler_thread_loop;libjvm.dylib`CompileBroker::invoke_compiler_on_method;libjvm.dylib`C2Compiler::compile_method;libjvm.dylib`Compile::Compile;libjvm.dylib`Compile::Optimize;libjvm.dylib`PhaseIdealLoop::build_and_optimize;libjvm.dylib`PhaseIterGVN::optimize;libjvm.dylib`PhaseIterGVN::transform_old;libjvm.dylib`RegionNode::Ideal;libjvm.dylib`PhaseIterGVN::subsume_node;libjvm.dylib`PhaseIterGVN::remove_globally_dead_node;libjvm.dylib`Node::replace_edge 1 3 | libsystem_pthread.dylib`thread_start;libsystem_pthread.dylib`_pthread_start;libsystem_pthread.dylib`_pthread_body;libjvm.dylib`java_start(Thread*);libjvm.dylib`JavaThread::run;libjvm.dylib`JavaThread::thread_main_inner;libjvm.dylib`thread_entry(JavaThread*, Thread*);libjvm.dylib`JavaCalls::call_virtual;libjvm.dylib`JavaCalls::call_virtual;libjvm.dylib`JavaCalls::call_helper;0x104ce54e7;0x104cecffd;0x104cecffd;0x104cecffd;0x104d08831 1 4 | -------------------------------------------------------------------------------- /tests/data/collapse-dtrace/results/java.txt: -------------------------------------------------------------------------------- 1 | test`main;test`foo;bar_[i];test`baz 1 2 | -------------------------------------------------------------------------------- /tests/data/collapse-dtrace/results/rust-names.txt: -------------------------------------------------------------------------------- 1 | libsystem_pthread.dylib`thread_start;libsystem_pthread.dylib`_pthread_start;libsystem_pthread.dylib`_pthread_body;rg`std::sys::unix::thread::Thread::new::thread_start;rg`std::sys_common::thread::start_thread;rg`>::call_box;rg`__rust_maybe_catch_panic;rg`std::panicking::try::do_call;rg`std::sys_common::backtrace::__rust_begin_short_backtrace;rg`ignore::walk::Worker::run;rg` as core::convert::From<&'a [T]>>::from 1 2 | libsystem_pthread.dylib`thread_start;libsystem_pthread.dylib`_pthread_start;libsystem_pthread.dylib`_pthread_body;rg`std::sys::unix::thread::Thread::new::thread_start;rg`std::sys_common::thread::start_thread;rg`>::call_box;rg`__rust_maybe_catch_panic;rg`std::panicking::try::do_call;rg`std::sys_common::backtrace::__rust_begin_short_backtrace;rg`ignore::walk::Worker::run;rg`::next;libsystem_platform.dylib`_platform_memset$VARIANT$Haswell 1 3 | libsystem_pthread.dylib`thread_start;libsystem_pthread.dylib`_pthread_start;libsystem_pthread.dylib`_pthread_body;rg`std::sys::unix::thread::Thread::new::thread_start;rg`std::sys_common::thread::start_thread;rg`>::call_box;rg`__rust_maybe_catch_panic;rg`std::panicking::try::do_call;rg`std::sys_common::backtrace::__rust_begin_short_backtrace;rg`ignore::walk::Worker::run;rg`ignore::dir::Ignore::add_child_path;rg`ignore::gitignore::GitignoreBuilder::add;rg`std::path::Path::to_path_buf;rg` as core::convert::From<&'a [T]>>::from;libsystem_malloc.dylib`malloc;libsystem_malloc.dylib`malloc_zone_malloc 1 4 | libsystem_pthread.dylib`thread_start;libsystem_pthread.dylib`_pthread_start;libsystem_pthread.dylib`_pthread_body;rg`std::sys::unix::thread::Thread::new::thread_start;rg`std::sys_common::thread::start_thread;rg`>::call_box;rg`__rust_maybe_catch_panic;rg`std::panicking::try::do_call;rg`std::sys_common::backtrace::__rust_begin_short_backtrace;rg`ignore::walk::Worker::run;rg`rg::search_parallel::_{{closure}}::_{{closure}};rg`>::search_impl;rg`grep_searcher::searcher::Searcher::search_path;rg`>::run;rg`>::fill;rg` as std::io::Read>::read;rg`encoding_rs_io::util::PossibleBom::as_slice 1 5 | libsystem_pthread.dylib`thread_start;libsystem_pthread.dylib`_pthread_start;libsystem_pthread.dylib`_pthread_body;rg`std::sys::unix::thread::Thread::new::thread_start;rg`std::sys_common::thread::start_thread;rg`>::call_box;rg`__rust_maybe_catch_panic;rg`std::panicking::try::do_call;rg`std::sys_common::backtrace::__rust_begin_short_backtrace;rg`ignore::walk::Worker::run;rg`rg::search_parallel::_{{closure}}::_{{closure}};rg`>::search_impl;rg`grep_searcher::searcher::Searcher::search_path;rg`std::fs::OpenOptions::_open;rg`std::sys::unix::fs::File::open;rg`>::reserve_internal 1 6 | libsystem_pthread.dylib`thread_start;libsystem_pthread.dylib`_pthread_start;libsystem_pthread.dylib`_pthread_body;rg`std::sys::unix::thread::Thread::new::thread_start;rg`std::sys_common::thread::start_thread;rg`>::call_box;rg`__rust_maybe_catch_panic;rg`std::panicking::try::do_call;rg`std::sys_common::backtrace::__rust_begin_short_backtrace;rg`ignore::walk::Worker::run;rg`rg::search_parallel::_{{closure}}::_{{closure}};rg`rg::subject::SubjectBuilder::build_from_result;rg`ignore::walk::DirEntry::is_stdin 1 7 | -------------------------------------------------------------------------------- /tests/data/collapse-dtrace/results/scope_with_no_argument_list.txt: -------------------------------------------------------------------------------- 1 | libsystem_pthread.dylib`thread_start;libsystem_pthread.dylib`_pthread_start;libsystem_pthread.dylib`_pthread_body;libjvm.dylib`java_start(Thread*);libjvm.dylib`JavaThread::run;libjvm.dylib`JavaThread::thread_main_inner;libjvm.dylib`CompileBroker::compiler_thread_loop;libjvm.dylib`CompileBroker::invoke_compiler_on_method;libjvm.dylib`C2Compiler::compile_method;libjvm.dylib`Compile::Compile;libjvm.dylib`Compile::Optimize;libjvm.dylib`PhaseIdealLoop::build_and_optimize;libjvm.dylib`PhaseIterGVN::optimize;libjvm.dylib`PhaseIterGVN::transform_old;libjvm.dylib`RegionNode::Ideal;libjvm.dylib`PhaseIterGVN::subsume_node;libjvm.dylib`PhaseIterGVN::remove_globally_dead_node;libjvm.dylib`Node::replace_edge 1 2 | libsystem_pthread.dylib`thread_start;libsystem_pthread.dylib`_pthread_start;libsystem_pthread.dylib`_pthread_body;libjvm.dylib`java_start(Thread*);libjvm.dylib`JavaThread::run;libjvm.dylib`JavaThread::thread_main_inner;libjvm.dylib`thread_entry(JavaThread*, Thread*);libjvm.dylib`JavaCalls::call_virtual;libjvm.dylib`JavaCalls::call_virtual;libjvm.dylib`JavaCalls::call_helper 1 3 | libzip.dylib`Java_java_util_zip_ZipFile_read;libzip.dylib`ZIP_Read;libsystem_kernel.dylib`read 2 4 | -------------------------------------------------------------------------------- /tests/data/collapse-dtrace/results/stack-ustack.txt: -------------------------------------------------------------------------------- 1 | libc.so.1`_lwp_start;libc.so.1`_thrp_setup;cru-ds-fdsync-v1`std::sys::unix::thread::Thread::new::thread_start;cru-ds-fdsync-v1`core::ops::function::FnOnce::call_once{{vtable.shim}};cru-ds-fdsync-v1`std::sys_common::backtrace::__rust_begin_short_backtrace;cru-ds-fdsync-v1`rayon_core::registry::ThreadBuilder::run;cru-ds-fdsync-v1`rayon_core::registry::WorkerThread::wait_until_cold;cru-ds-fdsync-v1` as rayon_core::job::Job>::execute;cru-ds-fdsync-v1`std::panicking::try;cru-ds-fdsync-v1`crucible_downstairs::extent::Extent::flush;cru-ds-fdsync-v1`::flush;cru-ds-fdsync-v1`std::fs::File::sync_all;libc.so.1`__fdsync;unix`sys_syscall;genunix`fdsync;genunix`fop_fsync;zfs`zfs_fsync;zfs`zil_commit;zfs`zil_commit_impl;zfs`zil_commit_itx_assign;zfs`zil_itx_assign;zfs`dmu_objset_ds 1 2 | libc.so.1`_lwp_start;libc.so.1`_thrp_setup;cru-ds-fdsync-v1`std::sys::unix::thread::Thread::new::thread_start;cru-ds-fdsync-v1`core::ops::function::FnOnce::call_once{{vtable.shim}};cru-ds-fdsync-v1`std::sys_common::backtrace::__rust_begin_short_backtrace;cru-ds-fdsync-v1`tokio::runtime::task::harness::Harness;cru-ds-fdsync-v1`tokio::runtime::task::core::Core;cru-ds-fdsync-v1`tokio::runtime::scheduler::multi_thread::worker::run;cru-ds-fdsync-v1`tokio::runtime::context::runtime::enter_runtime;cru-ds-fdsync-v1`tokio::runtime::context::scoped::Scoped;cru-ds-fdsync-v1`tokio::runtime::scheduler::multi_thread::worker::Context::run;cru-ds-fdsync-v1`tokio::runtime::scheduler::multi_thread::worker::Context::run_task;cru-ds-fdsync-v1`tokio::runtime::task::harness::Harness;cru-ds-fdsync-v1`tokio::runtime::task::core::Core;cru-ds-fdsync-v1`crucible_downstairs::Downstairs::run::_{{closure}};cru-ds-fdsync-v1`crucible_downstairs::ActiveConnection::do_work_if_ready::{{closure}};cru-ds-fdsync-v1`crucible_downstairs::ActiveConnection::do_work::{{closure}};cru-ds-fdsync-v1`crucible_downstairs::region::Region::region_write;cru-ds-fdsync-v1`crucible_downstairs::extent::Extent::write;cru-ds-fdsync-v1`::write;libc.so.1`__pwrite;unix`sys_syscall;genunix`pwrite;genunix`fop_write;zfs`zfs_write;zfs`dmu_tx_assign;zfs`dmu_tx_try_assign;zfs`dsl_dir_tempreserve_space;zfs`arc_memory_throttle 1 3 | libc.so.1`_lwp_start;libc.so.1`_thrp_setup;cru-ds-fdsync-v1`std::sys::unix::thread::Thread::new::thread_start;cru-ds-fdsync-v1`core::ops::function::FnOnce::call_once{{vtable.shim}};cru-ds-fdsync-v1`std::sys_common::backtrace::__rust_begin_short_backtrace;cru-ds-fdsync-v1`tokio::runtime::task::harness::Harness;cru-ds-fdsync-v1`tokio::runtime::task::core::Core;cru-ds-fdsync-v1`tokio::runtime::scheduler::multi_thread::worker::run;cru-ds-fdsync-v1`tokio::runtime::context::runtime::enter_runtime;cru-ds-fdsync-v1`tokio::runtime::context::scoped::Scoped;cru-ds-fdsync-v1`tokio::runtime::scheduler::multi_thread::worker::Context::run;cru-ds-fdsync-v1`tokio::runtime::scheduler::multi_thread::worker::Context::run_task;cru-ds-fdsync-v1`tokio::runtime::task::harness::Harness;cru-ds-fdsync-v1`tokio::runtime::task::core::Core;cru-ds-fdsync-v1`crucible_downstairs::recv_task::_{{closure}};cru-ds-fdsync-v1` as futures_core::stream::Stream>::poll_next::h375c4bf72398a9c9+0x451 7 | cru-ds-fdsync-v1` as core::future::future::Future>::poll::hdb622d175ae42c67+0x376 8 | cru-ds-fdsync-v1`crucible_downstairs::recv_task::_$u7b$$u7b$closure$u7d$$u7d$::h2313d0ca35c97b46 (.llvm.9599067906520872268)+0x1e8 9 | cru-ds-fdsync-v1`tokio::runtime::task::core::Core::poll::ha1b200ead37ecb81+0x3e 10 | cru-ds-fdsync-v1`tokio::runtime::task::harness::Harness::poll::h6096748dfbf63e3f+0x47 11 | cru-ds-fdsync-v1`tokio::runtime::scheduler::multi_thread::worker::Context::run_task::hbc7adf21738f2cc8+0x146 12 | cru-ds-fdsync-v1`tokio::runtime::scheduler::multi_thread::worker::Context::run::h8894624b64f9ebd1+0xabe 13 | cru-ds-fdsync-v1`tokio::runtime::context::scoped::Scoped::set::h7f53e80a35be279f+0x2b 14 | cru-ds-fdsync-v1`tokio::runtime::context::runtime::enter_runtime::ha36cde2fe36fc9dc+0x172 15 | cru-ds-fdsync-v1`tokio::runtime::scheduler::multi_thread::worker::run::heea3712ec5628e2e+0x4b 16 | cru-ds-fdsync-v1`tokio::runtime::task::core::Core::poll::h4fdbe1f8b2e046e9+0x43 17 | cru-ds-fdsync-v1`tokio::runtime::task::harness::Harness::poll::hdaaa15b8bfc6c3b5+0x45 18 | cru-ds-fdsync-v1`std::sys_common::backtrace::__rust_begin_short_backtrace::h07e5ebb552c48fa6+0x1b6 19 | cru-ds-fdsync-v1`core::ops::function::FnOnce::call_once{{vtable.shim}}::hfb58463ecc052a89+0x75 20 | cru-ds-fdsync-v1`std::sys::unix::thread::Thread::new::thread_start::h1783cbcbbf061711+0x29 21 | libc.so.1`_thrp_setup+0x77 22 | libc.so.1`_lwp_start 23 | 1 24 | 25 | zfs`arc_memory_throttle+0x1 26 | zfs`dsl_dir_tempreserve_space+0x9e 27 | zfs`dmu_tx_try_assign+0x149 28 | zfs`dmu_tx_assign+0x56 29 | zfs`zfs_write+0x475 30 | genunix`fop_write+0x5d 31 | genunix`pwrite+0x172 32 | unix`sys_syscall+0x17d 33 | 34 | libc.so.1`__pwrite+0xa 35 | cru-ds-fdsync-v1`::write::hef63fa757835e3ff+0x1621 36 | cru-ds-fdsync-v1`crucible_downstairs::extent::Extent::write::hfeced6743c01c23b+0x2fd 37 | cru-ds-fdsync-v1`crucible_downstairs::region::Region::region_write::hfbbb7f74fb6b1568+0x654 38 | cru-ds-fdsync-v1`crucible_downstairs::ActiveConnection::do_work::{{closure}}::h50e10780011359fc+0x1128 39 | cru-ds-fdsync-v1`crucible_downstairs::ActiveConnection::do_work_if_ready::{{closure}}::h65a7bded82d12d6c+0x148 40 | cru-ds-fdsync-v1`crucible_downstairs::Downstairs::run::_$u7b$$u7b$closure$u7d$$u7d$::h7a3087a6f836e6c7 (.llvm.14070482526595365531)+0x2012 41 | cru-ds-fdsync-v1`tokio::runtime::task::core::Core::poll::hf3cf7a78e14ca649+0x47 42 | cru-ds-fdsync-v1`tokio::runtime::task::harness::Harness::poll::h2d72b7b7afc40df2+0x49 43 | cru-ds-fdsync-v1`tokio::runtime::scheduler::multi_thread::worker::Context::run_task::hbc7adf21738f2cc8+0x190 44 | cru-ds-fdsync-v1`tokio::runtime::scheduler::multi_thread::worker::Context::run::h8894624b64f9ebd1+0xabe 45 | cru-ds-fdsync-v1`tokio::runtime::context::scoped::Scoped::set::h7f53e80a35be279f+0x2b 46 | cru-ds-fdsync-v1`tokio::runtime::context::runtime::enter_runtime::ha36cde2fe36fc9dc+0x172 47 | cru-ds-fdsync-v1`tokio::runtime::scheduler::multi_thread::worker::run::heea3712ec5628e2e+0x4b 48 | cru-ds-fdsync-v1`tokio::runtime::task::core::Core::poll::h4fdbe1f8b2e046e9+0x43 49 | cru-ds-fdsync-v1`tokio::runtime::task::harness::Harness::poll::hdaaa15b8bfc6c3b5+0x45 50 | cru-ds-fdsync-v1`std::sys_common::backtrace::__rust_begin_short_backtrace::h07e5ebb552c48fa6+0x1b6 51 | cru-ds-fdsync-v1`core::ops::function::FnOnce::call_once{{vtable.shim}}::hfb58463ecc052a89+0x75 52 | cru-ds-fdsync-v1`std::sys::unix::thread::Thread::new::thread_start::h1783cbcbbf061711+0x29 53 | libc.so.1`_thrp_setup+0x77 54 | libc.so.1`_lwp_start 55 | 1 56 | 57 | zfs`metaslab_group_alloc_verify+0x1 58 | zfs`zio_execute+0xa7 59 | genunix`taskq_thread+0x2a6 60 | unix`thread_start+0xb 61 | 62 | 1 63 | 64 | zfs`dmu_objset_ds+0x1 65 | zfs`zil_itx_assign+0x194 66 | zfs`zil_commit_itx_assign+0x65 67 | zfs`zil_commit_impl+0x26 68 | zfs`zil_commit+0x4b 69 | zfs`zfs_fsync+0xf6 70 | genunix`fop_fsync+0x4a 71 | genunix`fdsync+0xc4 72 | unix`sys_syscall+0x17d 73 | 74 | libc.so.1`__fdsync+0xa 75 | cru-ds-fdsync-v1`std::fs::File::sync_all::hcd4d0768a77cbc2e+0x14 76 | cru-ds-fdsync-v1`::flush::h68fcf1774758d74e+0x9c 77 | cru-ds-fdsync-v1`crucible_downstairs::extent::Extent::flush::h0eac5b95dfa4f5f0+0x472 78 | cru-ds-fdsync-v1`std::panicking::try::h7ba6611983f64757+0x4c 79 | cru-ds-fdsync-v1`_$LT$rayon_core..job..HeapJob$LT$BODY$GT$$u20$as$u20$rayon_core..job..Job$GT$::execute::h60b9d586fc4f8a0f (.llvm.8052073739315931670)+0x46 80 | cru-ds-fdsync-v1`rayon_core::registry::WorkerThread::wait_until_cold::haa78671c0e7aa9b1+0x50f 81 | cru-ds-fdsync-v1`rayon_core::registry::ThreadBuilder::run::hf28d413d115bded0+0x398 82 | cru-ds-fdsync-v1`std::sys_common::backtrace::__rust_begin_short_backtrace::hfcd6324a3e87fc1e+0x48 83 | cru-ds-fdsync-v1`core::ops::function::FnOnce::call_once{{vtable.shim}}::hbfa4fc2e086997af+0xb2 84 | cru-ds-fdsync-v1`std::sys::unix::thread::Thread::new::thread_start::h1783cbcbbf061711+0x29 85 | libc.so.1`_thrp_setup+0x77 86 | libc.so.1`_lwp_start 87 | 1 88 | -------------------------------------------------------------------------------- /tests/data/collapse-ghcprof/results/utf8.txt: -------------------------------------------------------------------------------- 1 | MAIN.MAIN 26 2 | MAIN.MAIN;GHC.Conc.Signal.CAF 0 3 | MAIN.MAIN;GHC.Event.Thread.CAF 0 4 | MAIN.MAIN;GHC.IO.Encoding.CAF 0 5 | MAIN.MAIN;GHC.IO.Encoding.Iconv.CAF 0 6 | MAIN.MAIN;GHC.IO.Handle.FD.CAF 0 7 | MAIN.MAIN;Lib.CAF:f_rPV 0 8 | MAIN.MAIN;Lib.CAF:一些函数1 0 9 | MAIN.MAIN;Lib.CAF:一些函数1;Lib.一二三 0 10 | MAIN.MAIN;Lib.CAF:一些函数1;Lib.一些函数 0 11 | MAIN.MAIN;Lib.CAF:一些函数1;Lib.你好世界 0 12 | MAIN.MAIN;Lib.你好世界 974 13 | MAIN.MAIN;Main.CAF:main1 0 14 | MAIN.MAIN;Main.CAF:main1;Main.main 0 15 | -------------------------------------------------------------------------------- /tests/data/collapse-ghcprof/results/utf8_bytes.txt: -------------------------------------------------------------------------------- 1 | MAIN.MAIN 456096 2 | MAIN.MAIN;GHC.Conc.Signal.CAF 640 3 | MAIN.MAIN;GHC.Event.Thread.CAF 1528 4 | MAIN.MAIN;GHC.IO.Encoding.CAF 2448 5 | MAIN.MAIN;GHC.IO.Encoding.Iconv.CAF 200 6 | MAIN.MAIN;GHC.IO.Handle.FD.CAF 34736 7 | MAIN.MAIN;Lib.CAF:f_rPV 256 8 | MAIN.MAIN;Lib.CAF:一些函数1 16 9 | MAIN.MAIN;Lib.CAF:一些函数1;Lib.一二三 0 10 | MAIN.MAIN;Lib.CAF:一些函数1;Lib.一些函数 0 11 | MAIN.MAIN;Lib.CAF:一些函数1;Lib.你好世界 0 12 | MAIN.MAIN;Lib.你好世界 90486357376 13 | MAIN.MAIN;Main.CAF:main1 16 14 | MAIN.MAIN;Main.CAF:main1;Main.main 0 15 | -------------------------------------------------------------------------------- /tests/data/collapse-ghcprof/results/utf8_ticks.txt: -------------------------------------------------------------------------------- 1 | MAIN.MAIN 510 2 | MAIN.MAIN;GHC.Conc.Signal.CAF 0 3 | MAIN.MAIN;GHC.Event.Thread.CAF 0 4 | MAIN.MAIN;GHC.IO.Encoding.CAF 0 5 | MAIN.MAIN;GHC.IO.Encoding.Iconv.CAF 0 6 | MAIN.MAIN;GHC.IO.Handle.FD.CAF 0 7 | MAIN.MAIN;Lib.CAF:f_rPV 0 8 | MAIN.MAIN;Lib.CAF:一些函数1 0 9 | MAIN.MAIN;Lib.CAF:一些函数1;Lib.一二三 0 10 | MAIN.MAIN;Lib.CAF:一些函数1;Lib.一些函数 0 11 | MAIN.MAIN;Lib.CAF:一些函数1;Lib.你好世界 0 12 | MAIN.MAIN;Lib.你好世界 19317 13 | MAIN.MAIN;Main.CAF:main1 0 14 | MAIN.MAIN;Main.CAF:main1;Main.main 0 15 | -------------------------------------------------------------------------------- /tests/data/collapse-ghcprof/utf8.prof: -------------------------------------------------------------------------------- 1 | Sun Aug 13 10:47 2023 Time and Allocation Profiling Report (Final) 2 | 3 | utf8test-exe +RTS -N -P -RTS 4 | 5 | total time = 10.25 secs (19827 ticks @ 1000 us, 32 processors) 6 | total alloc = 90,486,853,312 bytes (excludes profiling overheads) 7 | 8 | COST CENTRE MODULE SRC %time %alloc ticks bytes 9 | 10 | 你好世界 Lib src/Lib.hs:(14,1)-(15,41) 97.4 100.0 19317 90486357376 11 | MAIN MAIN 2.6 0.0 510 456096 12 | 13 | 14 | individual inherited 15 | COST CENTRE MODULE SRC no. entries %time %alloc %time %alloc ticks bytes 16 | 17 | MAIN MAIN 168 0 2.6 0.0 100.0 100.0 510 456096 18 | CAF GHC.Conc.Signal 289 0 0.0 0.0 0.0 0.0 0 640 19 | CAF GHC.IO.Encoding 279 0 0.0 0.0 0.0 0.0 0 2448 20 | CAF GHC.IO.Encoding.Iconv 277 0 0.0 0.0 0.0 0.0 0 200 21 | CAF GHC.IO.Handle.FD 269 0 0.0 0.0 0.0 0.0 0 34736 22 | CAF GHC.Event.Thread 228 0 0.0 0.0 0.0 0.0 0 1528 23 | CAF:f_rPV Lib 295 0 0.0 0.0 0.0 0.0 0 256 24 | CAF:main1 Main 334 0 0.0 0.0 0.0 0.0 0 16 25 | main Main app/Main.hs:6:1-11 336 1 0.0 0.0 0.0 0.0 0 0 26 | CAF:一些函数1 Lib 296 0 0.0 0.0 0.0 0.0 0 16 27 | 一二三 Lib src/Lib.hs:11:1-10 338 1 0.0 0.0 0.0 0.0 0 0 28 | 一些函数 Lib src/Lib.hs:8:1-10 337 1 0.0 0.0 0.0 0.0 0 0 29 | 你好世界 Lib src/Lib.hs:(14,1)-(15,41) 339 1 0.0 0.0 0.0 0.0 0 0 30 | 你好世界 Lib src/Lib.hs:(14,1)-(15,41) 340 0 97.4 100.0 97.4 100.0 19317 90486357376 31 | -------------------------------------------------------------------------------- /tests/data/collapse-guess/invalid-perf-with-empty-line-after-event-line.txt: -------------------------------------------------------------------------------- 1 | java 10488 9962.502413: 10101010 cpu-clock: 2 | 3 | ffffffffaee8017b fput+0xb ([kernel.kallsyms]) 4 | ffffffffaee7f61c ksys_write+0x9c ([kernel.kallsyms]) 5 | ffffffffaec041cb do_syscall_64+0x5b ([kernel.kallsyms]) 6 | ffffffffaf60008c entry_SYSCALL_64_after_hwframe+0x44 ([kernel.kallsyms]) 7 | 7f89e341d84f __GI___libc_write+0x4f (/usr/lib/libc-2.28.so) 8 | 5966605dd52f8e00 [unknown] ([unknown]) 9 | -------------------------------------------------------------------------------- /tests/data/collapse-guess/unknown-format.txt: -------------------------------------------------------------------------------- 1 | 2 | this 3 | is an unknown 4 | format 5 | -------------------------------------------------------------------------------- /tests/data/collapse-perf/empty-line.txt: -------------------------------------------------------------------------------- 1 | inline-counter 26037 8801.193547: 250000 cpu-clock:uhH: 2 | 401b97 main+0x1f (./tests/data/inline-counter/inline-counter) 3 | 4 | inline-counter 26037 8801.194312: 250000 cpu-clock:uhH: 5 | 6 | 401b94 main+0x1c (./tests/data/inline-counter/inline-counter) 7 | -------------------------------------------------------------------------------- /tests/data/collapse-perf/no-events.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonhoo/inferno/5ee7d6bb68ac9627010ef468c81f4f264cd0d399/tests/data/collapse-perf/no-events.txt -------------------------------------------------------------------------------- /tests/data/collapse-perf/results/cpp-stacks-std-function-collapsed.txt: -------------------------------------------------------------------------------- 1 | perf_stacks;[unknown];std::ostream::sentry::sentry 4614996 2 | perf_stacks;_dl_start;[[kernel.kallsyms]] 438507 3 | perf_stacks;_start;[[kernel.kallsyms]] 29005 4 | perf_stacks;_start;__libc_start_main;__libc_csu_init;_GLOBAL__sub_I_main;__static_initialization_and_destruction_0;std::ios_base::Init::Init;std::locale::locale;[libstdc++.so.6.0.25];std::locale::_Impl::_Impl;std::__timepunct::__timepunct;std::__timepunct::_M_initialize_timepunct 3946818 5 | perf_stacks;_start;__libc_start_main;main;std::endl >;std::ostream::put;_IO_new_file_overflow;_IO_new_do_write;new_do_write;_IO_new_file_write;__GI___libc_write;[[kernel.kallsyms]] 62360120 6 | perf_stacks;_start;__libc_start_main;main;std::function::operator;std::_Function_handler::_M_invoke 3306084 7 | perf_stacks;_start;__libc_start_main;main;std::ostream::flush 2947000 8 | perf_stacks;_start;_dl_start;_dl_start_final;_dl_sysdep_start;dl_main;_dl_map_object_deps;_dl_catch_exception;openaux;_dl_map_object;_dl_map_object_from_fd;_dl_map_segments;__mmap64;[[kernel.kallsyms]] 2925420 9 | -------------------------------------------------------------------------------- /tests/data/collapse-perf/results/go-stacks-collapsed.txt: -------------------------------------------------------------------------------- 1 | go;[unknown];[unknown];runtime.main;main.main;cmd/go/internal/run.runRun;cmd/go/internal/load.PackagesAndErrors;cmd/go/internal/load.loadPackage;cmd/go/internal/load.LoadImport;cmd/go/internal/load.(*Package).load;cmd/go/internal/load.LoadImport;cmd/go/internal/load.(*Package).load;cmd/go/internal/load.LoadImport;cmd/go/internal/load.(*Package).load;cmd/go/internal/load.LoadImport;cmd/go/internal/load.(*Package).load;cmd/go/internal/load.LoadImport;go/build.(*Context).Import;go/build.(*Context).matchFile;go/build.readImports;go/build.(*importReader).readKeyword;go/build.(*importReader).peekByte;go/build.(*importReader).readByte 250000 2 | go;[unknown];[unknown];runtime.main;main.main;cmd/go/internal/run.runRun;cmd/go/internal/load.PackagesAndErrors;cmd/go/internal/load.loadPackage;cmd/go/internal/load.LoadImport;cmd/go/internal/load.(*Package).load;cmd/go/internal/load.LoadImport;cmd/go/internal/load.(*Package).load;cmd/go/internal/load.LoadImport;cmd/go/internal/load.(*Package).load;cmd/go/internal/load.LoadImport;cmd/go/internal/load.(*Package).load;cmd/go/internal/load.LoadImport;go/build.(*Context).Import;go/parser.ParseFile;go/parser.(*parser).parseFile;go/parser.(*parser).expectSemi;go/parser.(*parser).next;go/parser.(*parser).consumeComment 250000 3 | go;[unknown];x_cgo_notify_runtime_init_done;runtime.main;main.init;cmd/go/internal/base.init;cmd/go/internal/cfg.init;go/build.init;go/doc.init;text/template.init;text/template.init.ializers;text/template.createValueFuncs;text/template.addValueFuncs;runtime.mapassign_faststr 250000 4 | go;[unknown];x_cgo_notify_runtime_init_done;runtime.main;main.init;cmd/go/internal/bug.init;cmd/go/internal/envcmd.init;cmd/go/internal/modload.init;cmd/go/internal/modfetch.init;cmd/go/internal/get.init;cmd/go/internal/work.init;cmd/go/internal/work.init.ializers;regexp.MustCompile;regexp.compile;regexp/syntax.Compile;runtime.growslice 250000 5 | go;[unknown];x_cgo_notify_runtime_init_done;runtime.main;main.init;cmd/go/internal/bug.init;cmd/go/internal/envcmd.init;cmd/go/internal/modload.init;cmd/go/internal/modfetch.init;cmd/go/internal/get.init;cmd/go/internal/work.init;cmd/go/internal/work.init.ializers;regexp.MustCompile;regexp.compile;regexp/syntax.Parse;regexp/syntax.(*parser).literal;regexp/syntax.(*parser).push;regexp/syntax.(*parser).maybeConcat;runtime.growslice 250000 6 | -------------------------------------------------------------------------------- /tests/data/collapse-perf/results/java-inline-collapsed.txt: -------------------------------------------------------------------------------- 1 | java;[unknown];__GI___libc_write 60606060 2 | java;[unknown];__GI___libc_write;entry_SYSCALL_64_after_hwframe;do_syscall_64 20202020 3 | java;[unknown];__GI___libc_write;entry_SYSCALL_64_after_hwframe;do_syscall_64;ksys_write;__fdget_pos;__fget_light 30303030 4 | java;[unknown];__GI___libc_write;entry_SYSCALL_64_after_hwframe;do_syscall_64;ksys_write;fput 10101010 5 | java;[unknown];__GI___libc_write;entry_SYSCALL_64_after_hwframe;do_syscall_64;ksys_write;vfs_write;__vfs_write;tty_write;n_tty_write 10101010 6 | java;[unknown];__GI___libc_write;entry_SYSCALL_64_after_hwframe;do_syscall_64;ksys_write;vfs_write;__vfs_write;tty_write;n_tty_write;_raw_spin_unlock_irqrestore 10101010 7 | java;[unknown];__GI___libc_write;entry_SYSCALL_64_after_hwframe;do_syscall_64;ksys_write;vfs_write;__vfs_write;tty_write;n_tty_write;pty_write;_raw_spin_unlock_irqrestore 20202020 8 | java;[unknown];__GI___libc_write;entry_SYSCALL_64_after_hwframe;do_syscall_64;ksys_write;vfs_write;__vfs_write;tty_write;tty_write_unlock 10101010 9 | java;start_thread;[libjli.so];[libjvm.so];[libjvm.so];[libjvm.so];call_stub;Interpreter;Interpreter;LCounter:::countTo;java/io/PrintStream:::println;java/io/PrintStream:::newLine_[i];java/io/OutputStreamWriter:::flushBuffer_[i];sun/nio/cs/StreamEncoder:::flushBuffer;sun/nio/cs/StreamEncoder:::implFlushBuffer_[i];sun/nio/cs/StreamEncoder:::writeBytes_[i];java/io/PrintStream:::write_[i];java/io/BufferedOutputStream:::flush_[i];java/io/BufferedOutputStream:::flushBuffer_[i];java/io/FileOutputStream:::write_[i] 10101010 10 | java;start_thread;[libjli.so];[libjvm.so];[libjvm.so];[libjvm.so];call_stub;Interpreter;Interpreter;LCounter:::countTo;java/io/PrintStream:::println;java/io/PrintStream:::newLine_[i];java/io/OutputStreamWriter:::flushBuffer_[i];sun/nio/cs/StreamEncoder:::flushBuffer;sun/nio/cs/StreamEncoder:::implFlushBuffer_[i];sun/nio/cs/StreamEncoder:::writeBytes_[i];java/io/PrintStream:::write_[i];java/io/BufferedOutputStream:::flush_[i];java/io/BufferedOutputStream:::flushBuffer_[i];java/io/FileOutputStream:::write_[i];java/io/FileOutputStream:::writeBytes;Java_java_io_FileOutputStream_writeBytes;[libjava.so];[libjvm.so];[libjvm.so] 10101010 11 | java;start_thread;[libjli.so];[libjvm.so];[libjvm.so];[libjvm.so];call_stub;Interpreter;Interpreter;LCounter:::countTo;java/io/PrintStream:::println;java/io/PrintStream:::print_[i];java/io/PrintStream:::write_[i];java/io/BufferedWriter:::flushBuffer;java/io/OutputStreamWriter:::write_[i];sun/nio/cs/StreamEncoder:::write_[i];sun/nio/cs/StreamEncoder:::implWrite_[i];sun/nio/cs/StreamEncoder:::implWrite_[i];java/nio/charset/CharsetEncoder:::encode_[i];sun/nio/cs/UTF_8$Encoder:::encodeLoop_[i] 10101010 12 | java;start_thread;[libjli.so];[libjvm.so];[libjvm.so];[libjvm.so];call_stub;Interpreter;Interpreter;LCounter:::countTo;java/io/PrintStream:::println;java/io/PrintStream:::print_[i];java/io/PrintStream:::write_[i];java/io/OutputStreamWriter:::flushBuffer_[i];sun/nio/cs/StreamEncoder:::flushBuffer;sun/nio/cs/StreamEncoder:::implFlushBuffer_[i];sun/nio/cs/StreamEncoder:::writeBytes_[i];java/io/PrintStream:::write_[i];java/io/BufferedOutputStream:::flush_[i] 10101010 13 | java;start_thread;[libjli.so];[libjvm.so];[libjvm.so];[libjvm.so];call_stub;Interpreter;Interpreter;LCounter:::countTo;java/io/PrintStream:::println;java/io/PrintStream:::print_[i];java/io/PrintStream:::write_[i];java/io/OutputStreamWriter:::flushBuffer_[i];sun/nio/cs/StreamEncoder:::flushBuffer;sun/nio/cs/StreamEncoder:::implFlushBuffer_[i];sun/nio/cs/StreamEncoder:::writeBytes_[i];java/io/PrintStream:::write_[i];java/io/BufferedOutputStream:::flush_[i];java/io/BufferedOutputStream:::flushBuffer_[i];java/io/FileOutputStream:::write_[i] 10101010 14 | -------------------------------------------------------------------------------- /tests/data/collapse-perf/results/no-events-collapsed.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonhoo/inferno/5ee7d6bb68ac9627010ef468c81f4f264cd0d399/tests/data/collapse-perf/results/no-events-collapsed.txt -------------------------------------------------------------------------------- /tests/data/collapse-perf/results/single-event-collapsed.txt: -------------------------------------------------------------------------------- 1 | boa_cli;[unknown];boa_cli::main;boa::realm::Realm::create;boa::realm::Realm::create_instrinsics;boa::builtins::console::create_constructor;boa::builtins::value::ValueData::set_field_slice;boa::builtins::value::ValueData::set_field;boa::builtins::object::internal_methods_trait::ObjectInternalMethods::set;::define_own_property;boa::builtins::value::to_value;::to_value;gc::Gc::new;gc::gc::GcBox::new;std::thread::local::LocalKey::with;std::thread::local::LocalKey::try_with;gc::gc::GcBox::new::_{{closure}};gc::gc::collect_garbage;gc::gc::collect_garbage::mark;gc::gc::GcBox::trace_inner; as gc::trace::Trace>::trace; as gc::trace::Trace>::trace; as gc::trace::Trace>::trace::mark;::trace;::trace::mark; as gc::trace::Trace>::trace; as gc::trace::Trace>::trace::mark;::trace;::trace::mark; as gc::trace::Trace>::trace;gc::gc::GcBox::trace_inner;::trace;::trace::mark; as gc::trace::Trace>::trace;::trace;::trace::mark; as gc::trace::Trace>::trace; as gc::trace::Trace>::trace::mark; as gc::trace::Trace>::trace; as gc::trace::Trace>::trace::mark;::trace;::trace::mark; as gc::trace::Trace>::trace; as gc::trace::Trace>::trace::mark; as gc::trace::Trace>::trace;gc::gc::GcBox::trace_inner;::trace;::trace::mark; as gc::trace::Trace>::trace;::trace;::trace::mark; as gc::trace::Trace>::trace; as gc::trace::Trace>::trace::mark; as gc::trace::Trace>::trace; as gc::trace::Trace>::trace::mark;::trace;::trace::mark; as gc::trace::Trace>::trace; as gc::trace::Trace>::trace::mark; as gc::trace::Trace>::trace;gc::gc::GcBox::trace_inner;::trace;::trace::mark; as gc::trace::Trace>::trace;::trace;::trace::mark; as gc::trace::Trace>::trace; as gc::trace::Trace>::trace::mark; as gc::trace::Trace>::trace; as gc::trace::Trace>::trace::mark;::trace;::trace::mark; as gc::trace::Trace>::trace; as gc::trace::Trace>::trace::mark; as gc::trace::Trace>::trace;gc::gc::GcBox::trace_inner;::trace;::trace::mark; as gc::trace::Trace>::trace; as gc::trace::Trace>::trace::mark; as gc::trace::Trace>::trace;::trace;::trace::mark;::trace;::trace::mark;::trace;::trace::mark; as gc::trace::Trace>::trace; as gc::trace::Trace>::trace::mark; as gc::trace::Trace>::trace 10101010 2 | -------------------------------------------------------------------------------- /tests/data/collapse-perf/results/single-line-stacks-collapsed.txt: -------------------------------------------------------------------------------- 1 | false;[unknown] 1 2 | false;_dl_start 1 3 | -------------------------------------------------------------------------------- /tests/data/collapse-perf/results/sourcepawn-jitdump-collapsed-jit.txt: -------------------------------------------------------------------------------- 1 | spshell;__libc_start_main;main;Execute;sp::ScriptedInvoker::Invoke;sp::PluginContext::Invoke;sp::Environment::Invoke;_[j];fib.smx::main_[j];fib.smx::fib_[j];fib.smx::fib_[j];fib.smx::fib_[j];fib.smx::fib_[j];fib.smx::fib_[j];fib.smx::fib_[j];fib.smx::fib_[j];fib.smx::fib_[j];fib.smx::fib_[j];fib.smx::fib_[j];fib.smx::fib_[j];fib.smx::fib_[j];fib.smx::fib_[j];fib.smx::fib_[j];fib.smx::fib_[j];fib.smx::fib_[j];fib.smx::fib_[j];fib.smx::fib_[j];fib.smx::fib_[j];fib.smx::fib_[j];fib.smx::fib_[j];fib.smx::fib_[j];fib.smx::fib_[j];fib.smx::fib_[j];fib.smx::fib_[j];fib.smx::fib_[j] 22145531 2 | -------------------------------------------------------------------------------- /tests/data/collapse-perf/results/versioned-vmlinux-collapsed-kernel.txt: -------------------------------------------------------------------------------- 1 | swapper;[vmlinux-5.4.14-cloudflare-2020.1.11]_[k];start_secondary_[k];cpu_startup_entry_[k];do_idle_[k];cpuidle_enter_[k];cpuidle_enter_state_[k];common_interrupt_[k];do_IRQ_[k];irq_exit_[k];__softirqentry_text_start_[k];net_rx_action_[k];mlx5e_napi_poll_[k];mlx5e_poll_rx_cq_[k];mlx5e_handle_rx_cqe_[k];mlx5e_skb_from_cqe_linear_[k];mlx5e_xdp_handle_[k];bpf_prog_2b956549c660136a_uni_l4lb 20834821 2 | -------------------------------------------------------------------------------- /tests/data/collapse-perf/single-line-stacks.txt: -------------------------------------------------------------------------------- 1 | false 39654 19115.489713: 1 cycles:u: ffffffff9b201293 [unknown] ([unknown]) 2 | false 39654 19115.489719: 1 cycles: 7fa68b1f2d84 _dl_start+0x4 (/usr/lib/ld-2.31.so) 3 | -------------------------------------------------------------------------------- /tests/data/collapse-perf/sourcepawn-jitdump.txt: -------------------------------------------------------------------------------- 1 | spshell 31714 451945.594655: 22145531 cycles: 2 | f719f4a4 fib.smx::fib+0x3c (/tmp/jitted-31714-4145673320.so) 3 | f719f54c fib.smx::fib+0xe4 (/tmp/jitted-31714-4145673320.so) 4 | f719f50b fib.smx::fib+0xa3 (/tmp/jitted-31714-4145673320.so) 5 | f719f54c fib.smx::fib+0xe4 (/tmp/jitted-31714-4145673320.so) 6 | f719f54c fib.smx::fib+0xe4 (/tmp/jitted-31714-4145673320.so) 7 | f719f54c fib.smx::fib+0xe4 (/tmp/jitted-31714-4145673320.so) 8 | f719f50b fib.smx::fib+0xa3 (/tmp/jitted-31714-4145673320.so) 9 | f719f54c fib.smx::fib+0xe4 (/tmp/jitted-31714-4145673320.so) 10 | f719f50b fib.smx::fib+0xa3 (/tmp/jitted-31714-4145673320.so) 11 | f719f54c fib.smx::fib+0xe4 (/tmp/jitted-31714-4145673320.so) 12 | f719f54c fib.smx::fib+0xe4 (/tmp/jitted-31714-4145673320.so) 13 | f719f54c fib.smx::fib+0xe4 (/tmp/jitted-31714-4145673320.so) 14 | f719f50b fib.smx::fib+0xa3 (/tmp/jitted-31714-4145673320.so) 15 | f719f54c fib.smx::fib+0xe4 (/tmp/jitted-31714-4145673320.so) 16 | f719f50b fib.smx::fib+0xa3 (/tmp/jitted-31714-4145673320.so) 17 | f719f50b fib.smx::fib+0xa3 (/tmp/jitted-31714-4145673320.so) 18 | f719f50b fib.smx::fib+0xa3 (/tmp/jitted-31714-4145673320.so) 19 | f719f50b fib.smx::fib+0xa3 (/tmp/jitted-31714-4145673320.so) 20 | f719f50b fib.smx::fib+0xa3 (/tmp/jitted-31714-4145673320.so) 21 | f719f54c fib.smx::fib+0xe4 (/tmp/jitted-31714-4145673320.so) 22 | f719f54c fib.smx::fib+0xe4 (/tmp/jitted-31714-4145673320.so) 23 | f719f54c fib.smx::fib+0xe4 (/tmp/jitted-31714-4145673320.so) 24 | f719f54c fib.smx::fib+0xe4 (/tmp/jitted-31714-4145673320.so) 25 | f719f54c fib.smx::fib+0xe4 (/tmp/jitted-31714-4145673320.so) 26 | f719f54c fib.smx::fib+0xe4 (/tmp/jitted-31714-4145673320.so) 27 | f719f54c fib.smx::fib+0xe4 (/tmp/jitted-31714-4145673320.so) 28 | f719f119 fib.smx::main+0x91 (/tmp/jitted-31714-4145672328.so) 29 | f719f06f +0x27 (/tmp/jitted-31714-4145672264.so) 30 | 8059187 sp::Environment::Invoke+0x307 (/root/code/sourcepawn/build/vm/spshell/linux-x86/spshell) 31 | 806d5d8 sp::PluginContext::Invoke+0x378 (/root/code/sourcepawn/build/vm/spshell/linux-x86/spshell) 32 | 8078bcc sp::ScriptedInvoker::Invoke+0x60c (/root/code/sourcepawn/build/vm/spshell/linux-x86/spshell) 33 | 804b413 Execute+0x3d3 (/root/code/sourcepawn/build/vm/spshell/linux-x86/spshell) 34 | 804ae7e main+0x61e (/root/code/sourcepawn/build/vm/spshell/linux-x86/spshell) 35 | f7abc8e9 __libc_start_main+0xf9 (/usr/lib/libc-2.29.so) 36 | 37 | -------------------------------------------------------------------------------- /tests/data/collapse-perf/versioned-vmlinux.txt: -------------------------------------------------------------------------------- 1 | swapper 0 [012] 2281339.604067: 20834821 cycles: 2 | ffffffffc09ace93 bpf_prog_2b956549c660136a_uni_l4lb+0xb80 (bpf_prog_2b956549c660136a_uni_l4lb) 3 | ffffffffc087cbc9 mlx5e_xdp_handle+0xa9 (/lib/modules/5.4.14-cloudflare-2020.1.11/kernel/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.ko) 4 | ffffffffc0878d87 mlx5e_skb_from_cqe_linear+0xc7 (/lib/modules/5.4.14-cloudflare-2020.1.11/kernel/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.ko) 5 | ffffffffc087a254 mlx5e_handle_rx_cqe+0x64 (/lib/modules/5.4.14-cloudflare-2020.1.11/kernel/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.ko) 6 | ffffffffc087bb48 mlx5e_poll_rx_cq+0x808 (/lib/modules/5.4.14-cloudflare-2020.1.11/kernel/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.ko) 7 | ffffffffc087beee mlx5e_napi_poll+0xde (/lib/modules/5.4.14-cloudflare-2020.1.11/kernel/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.ko) 8 | ffffffffb8f03eaa net_rx_action+0x13a (/usr/lib/debug/boot/vmlinux-5.4.14-cloudflare-2020.1.11) 9 | ffffffffb94000e0 __softirqentry_text_start+0xe0 (/usr/lib/debug/boot/vmlinux-5.4.14-cloudflare-2020.1.11) 10 | ffffffffb886c8c0 irq_exit+0xa0 (/usr/lib/debug/boot/vmlinux-5.4.14-cloudflare-2020.1.11) 11 | ffffffffb9201908 do_IRQ+0x58 (/usr/lib/debug/boot/vmlinux-5.4.14-cloudflare-2020.1.11) 12 | ffffffffb9200a0f common_interrupt+0xf (/usr/lib/debug/boot/vmlinux-5.4.14-cloudflare-2020.1.11) 13 | ffffffffb8ebc022 cpuidle_enter_state+0xb2 (/usr/lib/debug/boot/vmlinux-5.4.14-cloudflare-2020.1.11) 14 | ffffffffb8ebc3c9 cpuidle_enter+0x29 (/usr/lib/debug/boot/vmlinux-5.4.14-cloudflare-2020.1.11) 15 | ffffffffb88960f8 do_idle+0x1b8 (/usr/lib/debug/boot/vmlinux-5.4.14-cloudflare-2020.1.11) 16 | ffffffffb88962c9 cpu_startup_entry+0x19 (/usr/lib/debug/boot/vmlinux-5.4.14-cloudflare-2020.1.11) 17 | ffffffffb8841383 start_secondary+0x143 (/usr/lib/debug/boot/vmlinux-5.4.14-cloudflare-2020.1.11) 18 | ffffffffb88000d4 [unknown] (/usr/lib/debug/boot/vmlinux-5.4.14-cloudflare-2020.1.11) 19 | -------------------------------------------------------------------------------- /tests/data/collapse-perf/weird-stack-line.txt: -------------------------------------------------------------------------------- 1 | inline-counter 26037 8801.193547: 250000 cpu-clock:uhH: 2 | 401b97 main+0x1f (./tests/data/inline-counter/inline-counter) 3 | 4 | inline-counter 26037 8801.194312: 250000 cpu-clock:uhH: 5 | THIS_IS_A_WEIRD_LINE 6 | -------------------------------------------------------------------------------- /tests/data/collapse-recursive/basic.txt: -------------------------------------------------------------------------------- 1 | main;recursive;recursive;recursive;helper 1 2 | main;recursive;recursive;helper 2 3 | main;not;recursive 4 -------------------------------------------------------------------------------- /tests/data/collapse-recursive/results/basic-collapsed.txt: -------------------------------------------------------------------------------- 1 | main;not;recursive 4 2 | main;recursive;helper 3 -------------------------------------------------------------------------------- /tests/data/collapse-sample/bad-stack-line.txt: -------------------------------------------------------------------------------- 1 | Analysis of sampling rg (pid 64751) every 1 millisecond 2 | Process: rg [64751] 3 | Path: /usr/local/Cellar/ripgrep/11.0.1/bin/rg 4 | Load Address: 0x103746000 5 | Identifier: rg 6 | Version: 0 7 | Code Type: X86-64 8 | Parent Process: zsh [50523] 9 | 10 | Date/Time: 2019-07-04 10:21:28.347 -0600 11 | Launch Time: 2019-07-04 10:21:15.470 -0600 12 | OS Version: Mac OS X 10.14.5 (18F203) 13 | Report Version: 7 14 | Analysis Tool: /usr/bin/sample 15 | 16 | Physical footprint: 227.5M 17 | Physical footprint (peak): 227.5M 18 | ---- 19 | 20 | Call graph: 21 | 825 Thread_15758523 DispatchQueue_1: com.apple.main-thread (serial) 22 | + BAD 23 | + 825 main (in rg) + 41 [0x10384b549] 24 | + 825 std::rt::lang_start_internal::hd2693a01169d6aa1 (in rg) + 334 [0x103a0db1e] 25 | + 825 __rust_maybe_catch_panic (in rg) + 31 [0x103a230cf] 26 | + 825 std::panicking::try::do_call::hab896c32750930ec (.llvm.7886897009887049720) (in rg) + 24 [0x103a22258] 27 | + 825 std::rt::lang_start::_$u7b$$u7b$closure$u7d$$u7d$::h7cd9ec399e9db3f7 (in rg) + 6 [0x10381bb66] 28 | + 825 rg::main::h6909bd3a32e27a08 (in rg) + 34 [0x1038445a2] 29 | + 825 rg::try_main::h1b50b8f6fd4186a2 (in rg) + 13818 [0x103847c3a] 30 | + 825 ignore::walk::WalkParallel::run::h9c94d86e305a5f70 (in rg) + 3616 [0x1037cb520] 31 | + 825 _$LT$std..thread..JoinHandle$LT$T$GT$$GT$::join::hca6aa63e512626da (in rg) + 72 [0x103837e88] 32 | + 825 std::sys::unix::thread::Thread::join::h9bc404ce70591d96 (in rg) + 16 [0x103a10800] 33 | + 825 _pthread_join (in libsystem_pthread.dylib) + 358 [0x7fff739c76de] 34 | + 825 __ulock_wait (in libsystem_kernel.dylib) + 10 [0x7fff739069de] 35 | 36 | Total number in stack (recursive counted multiple, when >=5): 37 | REMOVED EVERYTHING BELOW HERE BECAUSE IT'S NOT USED 38 | -------------------------------------------------------------------------------- /tests/data/collapse-sample/end-before-call-graph-end.txt: -------------------------------------------------------------------------------- 1 | Analysis of sampling rg (pid 64751) every 1 millisecond 2 | Process: rg [64751] 3 | Path: /usr/local/Cellar/ripgrep/11.0.1/bin/rg 4 | Load Address: 0x103746000 5 | Identifier: rg 6 | Version: 0 7 | Code Type: X86-64 8 | Parent Process: zsh [50523] 9 | 10 | Date/Time: 2019-07-04 10:21:28.347 -0600 11 | Launch Time: 2019-07-04 10:21:15.470 -0600 12 | OS Version: Mac OS X 10.14.5 (18F203) 13 | Report Version: 7 14 | Analysis Tool: /usr/bin/sample 15 | 16 | Physical footprint: 227.5M 17 | Physical footprint (peak): 227.5M 18 | ---- 19 | 20 | Call graph: 21 | 825 Thread_15758523 DispatchQueue_1: com.apple.main-thread (serial) 22 | + 825 start (in libdyld.dylib) + 1 [0x7fff737cf3d5] 23 | + 825 main (in rg) + 41 [0x10384b549] 24 | + 825 std::rt::lang_start_internal::hd2693a01169d6aa1 (in rg) + 334 [0x103a0db1e] 25 | + 825 __rust_maybe_catch_panic (in rg) + 31 [0x103a230cf] 26 | + 825 std::panicking::try::do_call::hab896c32750930ec (.llvm.7886897009887049720) (in rg) + 24 [0x103a22258] 27 | + 825 std::rt::lang_start::_$u7b$$u7b$closure$u7d$$u7d$::h7cd9ec399e9db3f7 (in rg) + 6 [0x10381bb66] 28 | + 825 rg::main::h6909bd3a32e27a08 (in rg) + 34 [0x1038445a2] 29 | + 825 rg::try_main::h1b50b8f6fd4186a2 (in rg) + 13818 [0x103847c3a] 30 | + 825 ignore::walk::WalkParallel::run::h9c94d86e305a5f70 (in rg) + 3616 [0x1037cb520] 31 | + 825 _$LT$std..thread..JoinHandle$LT$T$GT$$GT$::join::hca6aa63e512626da (in rg) + 72 [0x103837e88] 32 | + 825 std::sys::unix::thread::Thread::join::h9bc404ce70591d96 (in rg) + 16 [0x103a10800] 33 | + 825 _pthread_join (in libsystem_pthread.dylib) + 358 [0x7fff739c76de] 34 | + 825 __ulock_wait (in libsystem_kernel.dylib) + 10 [0x7fff739069de] 35 | -------------------------------------------------------------------------------- /tests/data/collapse-sample/end-before-call-graph-start.txt: -------------------------------------------------------------------------------- 1 | Analysis of sampling rg (pid 64751) every 1 millisecond 2 | Process: rg [64751] 3 | Path: /usr/local/Cellar/ripgrep/11.0.1/bin/rg 4 | Load Address: 0x103746000 5 | Identifier: rg 6 | Version: 0 7 | Code Type: X86-64 8 | Parent Process: zsh [50523] 9 | 10 | Date/Time: 2019-07-04 10:21:28.347 -0600 11 | Launch Time: 2019-07-04 10:21:15.470 -0600 12 | OS Version: Mac OS X 10.14.5 (18F203) 13 | Report Version: 7 14 | Analysis Tool: /usr/bin/sample 15 | 16 | Physical footprint: 227.5M 17 | Physical footprint (peak): 227.5M 18 | ---- 19 | -------------------------------------------------------------------------------- /tests/data/collapse-sample/invalid-samples-field.txt: -------------------------------------------------------------------------------- 1 | Analysis of sampling rg (pid 64751) every 1 millisecond 2 | Process: rg [64751] 3 | Path: /usr/local/Cellar/ripgrep/11.0.1/bin/rg 4 | Load Address: 0x103746000 5 | Identifier: rg 6 | Version: 0 7 | Code Type: X86-64 8 | Parent Process: zsh [50523] 9 | 10 | Date/Time: 2019-07-04 10:21:28.347 -0600 11 | Launch Time: 2019-07-04 10:21:15.470 -0600 12 | OS Version: Mac OS X 10.14.5 (18F203) 13 | Report Version: 7 14 | Analysis Tool: /usr/bin/sample 15 | 16 | Physical footprint: 227.5M 17 | Physical footprint (peak): 227.5M 18 | ---- 19 | 20 | Call graph: 21 | 825 Thread_15758523 DispatchQueue_1: com.apple.main-thread (serial) 22 | + start (in libdyld.dylib) + 1 [0x7fff737cf3d5] 23 | + 825 main (in rg) + 41 [0x10384b549] 24 | + 825 std::rt::lang_start_internal::hd2693a01169d6aa1 (in rg) + 334 [0x103a0db1e] 25 | + 825 __rust_maybe_catch_panic (in rg) + 31 [0x103a230cf] 26 | + 825 std::panicking::try::do_call::hab896c32750930ec (.llvm.7886897009887049720) (in rg) + 24 [0x103a22258] 27 | + 825 std::rt::lang_start::_$u7b$$u7b$closure$u7d$$u7d$::h7cd9ec399e9db3f7 (in rg) + 6 [0x10381bb66] 28 | + 825 rg::main::h6909bd3a32e27a08 (in rg) + 34 [0x1038445a2] 29 | + 825 rg::try_main::h1b50b8f6fd4186a2 (in rg) + 13818 [0x103847c3a] 30 | + 825 ignore::walk::WalkParallel::run::h9c94d86e305a5f70 (in rg) + 3616 [0x1037cb520] 31 | + 825 _$LT$std..thread..JoinHandle$LT$T$GT$$GT$::join::hca6aa63e512626da (in rg) + 72 [0x103837e88] 32 | + 825 std::sys::unix::thread::Thread::join::h9bc404ce70591d96 (in rg) + 16 [0x103a10800] 33 | + 825 _pthread_join (in libsystem_pthread.dylib) + 358 [0x7fff739c76de] 34 | + 825 __ulock_wait (in libsystem_kernel.dylib) + 10 [0x7fff739069de] 35 | 36 | Total number in stack (recursive counted multiple, when >=5): 37 | REMOVED EVERYTHING BELOW HERE BECAUSE IT'S NOT USED 38 | -------------------------------------------------------------------------------- /tests/data/collapse-sample/large.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonhoo/inferno/5ee7d6bb68ac9627010ef468c81f4f264cd0d399/tests/data/collapse-sample/large.txt.gz -------------------------------------------------------------------------------- /tests/data/collapse-sample/no-four-spaces.txt: -------------------------------------------------------------------------------- 1 | Analysis of sampling rg (pid 64751) every 1 millisecond 2 | Process: rg [64751] 3 | Path: /usr/local/Cellar/ripgrep/11.0.1/bin/rg 4 | Load Address: 0x103746000 5 | Identifier: rg 6 | Version: 0 7 | Code Type: X86-64 8 | Parent Process: zsh [50523] 9 | 10 | Date/Time: 2019-07-04 10:21:28.347 -0600 11 | Launch Time: 2019-07-04 10:21:15.470 -0600 12 | OS Version: Mac OS X 10.14.5 (18F203) 13 | Report Version: 7 14 | Analysis Tool: /usr/bin/sample 15 | 16 | Physical footprint: 227.5M 17 | Physical footprint (peak): 227.5M 18 | ---- 19 | 20 | Call graph: 21 | 825 Thread_15758523 DispatchQueue_1: com.apple.main-thread (serial) 22 | + start (in libdyld.dylib) + 1 [0x7fff737cf3d5] 23 | + 825 main (in rg) + 41 [0x10384b549] 24 | + 825 std::rt::lang_start_internal::hd2693a01169d6aa1 (in rg) + 334 [0x103a0db1e] 25 | + 825 __rust_maybe_catch_panic (in rg) + 31 [0x103a230cf] 26 | + 825 std::panicking::try::do_call::hab896c32750930ec (.llvm.7886897009887049720) (in rg) + 24 [0x103a22258] 27 | + 825 std::rt::lang_start::_$u7b$$u7b$closure$u7d$$u7d$::h7cd9ec399e9db3f7 (in rg) + 6 [0x10381bb66] 28 | + 825 rg::main::h6909bd3a32e27a08 (in rg) + 34 [0x1038445a2] 29 | + 825 rg::try_main::h1b50b8f6fd4186a2 (in rg) + 13818 [0x103847c3a] 30 | + 825 ignore::walk::WalkParallel::run::h9c94d86e305a5f70 (in rg) + 3616 [0x1037cb520] 31 | + 825 _$LT$std..thread..JoinHandle$LT$T$GT$$GT$::join::hca6aa63e512626da (in rg) + 72 [0x103837e88] 32 | + 825 std::sys::unix::thread::Thread::join::h9bc404ce70591d96 (in rg) + 16 [0x103a10800] 33 | + 825 _pthread_join (in libsystem_pthread.dylib) + 358 [0x7fff739c76de] 34 | + 825 __ulock_wait (in libsystem_kernel.dylib) + 10 [0x7fff739069de] 35 | 36 | Total number in stack (recursive counted multiple, when >=5): 37 | REMOVED EVERYTHING BELOW HERE BECAUSE IT'S NOT USED 38 | -------------------------------------------------------------------------------- /tests/data/collapse-sample/odd-indentation.txt: -------------------------------------------------------------------------------- 1 | Analysis of sampling rg (pid 64751) every 1 millisecond 2 | Process: rg [64751] 3 | Path: /usr/local/Cellar/ripgrep/11.0.1/bin/rg 4 | Load Address: 0x103746000 5 | Identifier: rg 6 | Version: 0 7 | Code Type: X86-64 8 | Parent Process: zsh [50523] 9 | 10 | Date/Time: 2019-07-04 10:21:28.347 -0600 11 | Launch Time: 2019-07-04 10:21:15.470 -0600 12 | OS Version: Mac OS X 10.14.5 (18F203) 13 | Report Version: 7 14 | Analysis Tool: /usr/bin/sample 15 | 16 | Physical footprint: 227.5M 17 | Physical footprint (peak): 227.5M 18 | ---- 19 | 20 | Call graph: 21 | 825 Thread_15758523 DispatchQueue_1: com.apple.main-thread (serial) 22 | + 825 start (in libdyld.dylib) + 1 [0x7fff737cf3d5] 23 | + 825 main (in rg) + 41 [0x10384b549] 24 | + 825 std::rt::lang_start_internal::hd2693a01169d6aa1 (in rg) + 334 [0x103a0db1e] 25 | + 825 __rust_maybe_catch_panic (in rg) + 31 [0x103a230cf] 26 | + 825 std::panicking::try::do_call::hab896c32750930ec (.llvm.7886897009887049720) (in rg) + 24 [0x103a22258] 27 | + 825 std::rt::lang_start::_$u7b$$u7b$closure$u7d$$u7d$::h7cd9ec399e9db3f7 (in rg) + 6 [0x10381bb66] 28 | + 825 rg::main::h6909bd3a32e27a08 (in rg) + 34 [0x1038445a2] 29 | + 825 rg::try_main::h1b50b8f6fd4186a2 (in rg) + 13818 [0x103847c3a] 30 | + 825 ignore::walk::WalkParallel::run::h9c94d86e305a5f70 (in rg) + 3616 [0x1037cb520] 31 | + 825 _$LT$std..thread..JoinHandle$LT$T$GT$$GT$::join::hca6aa63e512626da (in rg) + 72 [0x103837e88] 32 | + 825 std::sys::unix::thread::Thread::join::h9bc404ce70591d96 (in rg) + 16 [0x103a10800] 33 | + 825 _pthread_join (in libsystem_pthread.dylib) + 358 [0x7fff739c76de] 34 | + 825 __ulock_wait (in libsystem_kernel.dylib) + 10 [0x7fff739069de] 35 | 36 | Total number in stack (recursive counted multiple, when >=5): 37 | REMOVED EVERYTHING BELOW HERE BECAUSE IT'S NOT USED 38 | -------------------------------------------------------------------------------- /tests/data/collapse-sample/skipped-indentation.txt: -------------------------------------------------------------------------------- 1 | Analysis of sampling rg (pid 64751) every 1 millisecond 2 | Process: rg [64751] 3 | Path: /usr/local/Cellar/ripgrep/11.0.1/bin/rg 4 | Load Address: 0x103746000 5 | Identifier: rg 6 | Version: 0 7 | Code Type: X86-64 8 | Parent Process: zsh [50523] 9 | 10 | Date/Time: 2019-07-04 10:21:28.347 -0600 11 | Launch Time: 2019-07-04 10:21:15.470 -0600 12 | OS Version: Mac OS X 10.14.5 (18F203) 13 | Report Version: 7 14 | Analysis Tool: /usr/bin/sample 15 | 16 | Physical footprint: 227.5M 17 | Physical footprint (peak): 227.5M 18 | ---- 19 | 20 | Call graph: 21 | 825 Thread_15758523 DispatchQueue_1: com.apple.main-thread (serial) 22 | + 825 start (in libdyld.dylib) + 1 [0x7fff737cf3d5] 23 | + 825 main (in rg) + 41 [0x10384b549] 24 | + 825 std::rt::lang_start_internal::hd2693a01169d6aa1 (in rg) + 334 [0x103a0db1e] 25 | + 825 __rust_maybe_catch_panic (in rg) + 31 [0x103a230cf] 26 | + 825 std::panicking::try::do_call::hab896c32750930ec (.llvm.7886897009887049720) (in rg) + 24 [0x103a22258] 27 | + 825 std::rt::lang_start::_$u7b$$u7b$closure$u7d$$u7d$::h7cd9ec399e9db3f7 (in rg) + 6 [0x10381bb66] 28 | + 825 rg::main::h6909bd3a32e27a08 (in rg) + 34 [0x1038445a2] 29 | + 825 rg::try_main::h1b50b8f6fd4186a2 (in rg) + 13818 [0x103847c3a] 30 | + 825 ignore::walk::WalkParallel::run::h9c94d86e305a5f70 (in rg) + 3616 [0x1037cb520] 31 | + 825 _$LT$std..thread..JoinHandle$LT$T$GT$$GT$::join::hca6aa63e512626da (in rg) + 72 [0x103837e88] 32 | + 825 std::sys::unix::thread::Thread::join::h9bc404ce70591d96 (in rg) + 16 [0x103a10800] 33 | + 825 _pthread_join (in libsystem_pthread.dylib) + 358 [0x7fff739c76de] 34 | + 825 __ulock_wait (in libsystem_kernel.dylib) + 10 [0x7fff739069de] 35 | 36 | Total number in stack (recursive counted multiple, when >=5): 37 | REMOVED EVERYTHING BELOW HERE BECAUSE IT'S NOT USED 38 | -------------------------------------------------------------------------------- /tests/data/collapse-sample/stack-line-only-indent-chars.txt: -------------------------------------------------------------------------------- 1 | Analysis of sampling rg (pid 64751) every 1 millisecond 2 | Process: rg [64751] 3 | Path: /usr/local/Cellar/ripgrep/11.0.1/bin/rg 4 | Load Address: 0x103746000 5 | Identifier: rg 6 | Version: 0 7 | Code Type: X86-64 8 | Parent Process: zsh [50523] 9 | 10 | Date/Time: 2019-07-04 10:21:28.347 -0600 11 | Launch Time: 2019-07-04 10:21:15.470 -0600 12 | OS Version: Mac OS X 10.14.5 (18F203) 13 | Report Version: 7 14 | Analysis Tool: /usr/bin/sample 15 | 16 | Physical footprint: 227.5M 17 | Physical footprint (peak): 227.5M 18 | ---- 19 | 20 | Call graph: 21 | 825 Thread_15758523 DispatchQueue_1: com.apple.main-thread (serial) 22 | + |:! 23 | + 825 main (in rg) + 41 [0x10384b549] 24 | + 825 std::rt::lang_start_internal::hd2693a01169d6aa1 (in rg) + 334 [0x103a0db1e] 25 | + 825 __rust_maybe_catch_panic (in rg) + 31 [0x103a230cf] 26 | + 825 std::panicking::try::do_call::hab896c32750930ec (.llvm.7886897009887049720) (in rg) + 24 [0x103a22258] 27 | + 825 std::rt::lang_start::_$u7b$$u7b$closure$u7d$$u7d$::h7cd9ec399e9db3f7 (in rg) + 6 [0x10381bb66] 28 | + 825 rg::main::h6909bd3a32e27a08 (in rg) + 34 [0x1038445a2] 29 | + 825 rg::try_main::h1b50b8f6fd4186a2 (in rg) + 13818 [0x103847c3a] 30 | + 825 ignore::walk::WalkParallel::run::h9c94d86e305a5f70 (in rg) + 3616 [0x1037cb520] 31 | + 825 _$LT$std..thread..JoinHandle$LT$T$GT$$GT$::join::hca6aa63e512626da (in rg) + 72 [0x103837e88] 32 | + 825 std::sys::unix::thread::Thread::join::h9bc404ce70591d96 (in rg) + 16 [0x103a10800] 33 | + 825 _pthread_join (in libsystem_pthread.dylib) + 358 [0x7fff739c76de] 34 | + 825 __ulock_wait (in libsystem_kernel.dylib) + 10 [0x7fff739069de] 35 | 36 | Total number in stack (recursive counted multiple, when >=5): 37 | REMOVED EVERYTHING BELOW HERE BECAUSE IT'S NOT USED 38 | -------------------------------------------------------------------------------- /tests/data/collapse-vsprof/empty-file.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonhoo/inferno/5ee7d6bb68ac9627010ef468c81f4f264cd0d399/tests/data/collapse-vsprof/empty-file.csv -------------------------------------------------------------------------------- /tests/data/collapse-vsprof/incorrect-header.csv: -------------------------------------------------------------------------------- 1 | This is not a correct header 2 | -------------------------------------------------------------------------------- /tests/data/collapse-vsprof/invalid-depth.csv: -------------------------------------------------------------------------------- 1 | Level,Function Name,Number of Calls,Elapsed Inclusive Time %,Elapsed Exclusive Time %,Avg Elapsed Inclusive Time,Avg Elapsed Exclusive Time,Module Name, 2 | 0,"Raytracer.exe",0,100.00,0.00,0.00,0.00,"", 3 | x,"Raytracer.Raytracer.Tracer.b__5_0(int32)",471,91.25,18.39,401.92,81.02,"Raytracer.exe", 4 | -------------------------------------------------------------------------------- /tests/data/collapse-vsprof/invalid-function-name.csv: -------------------------------------------------------------------------------- 1 | Level,Function Name,Number of Calls,Elapsed Inclusive Time %,Elapsed Exclusive Time %,Avg Elapsed Inclusive Time,Avg Elapsed Exclusive Time,Module Name, 2 | 0,"Raytracer.exe",0,100.00,0.00,0.00,0.00,"", 3 | 1,"Raytracer.Raytracer.Tracer.b__5_0(int32) 4 | -------------------------------------------------------------------------------- /tests/data/collapse-vsprof/invalid-number-of-calls.csv: -------------------------------------------------------------------------------- 1 | Level,Function Name,Number of Calls,Elapsed Inclusive Time %,Elapsed Exclusive Time %,Avg Elapsed Inclusive Time,Avg Elapsed Exclusive Time,Module Name, 2 | 0,"Raytracer.exe",0,100.00,0.00,0.00,0.00,"", 3 | 1,"Raytracer.Raytracer.Tracer.b__5_0(int32)","4,718.25",91.25,18.39,401.92,81.02,"Raytracer.exe", 4 | -------------------------------------------------------------------------------- /tests/data/collapse-vsprof/missing-function-name.csv: -------------------------------------------------------------------------------- 1 | Level,Function Name,Number of Calls,Elapsed Inclusive Time %,Elapsed Exclusive Time %,Avg Elapsed Inclusive Time,Avg Elapsed Exclusive Time,Module Name, 2 | 0 3 | -------------------------------------------------------------------------------- /tests/data/collapse-vtune/bad-stack-line.csv: -------------------------------------------------------------------------------- 1 | Function Stack,CPU Time:Self,Module 2 | Total,0.0,[Unknown] 3 | RtlUserThreadStart,0.0,ntdll.dll 4 | BaseThreadInitThunk,0.0,KERNEL32.DLL 5 | _scrt_common_main_seh,0.0,inferno-collapse-dtrace.exe 6 | main,0.0,inferno-collapse-dtrace.exe 7 | BAD STACK LINE 8 | std::rt::lang_start_internal,0.0,inferno-collapse-dtrace.exe 9 | rust_maybe_catch_panic,0.0,inferno-collapse-dtrace.exe 10 | "std::panicking::try::do_call",0.0,inferno-collapse-dtrace.exe 11 | "std::rt::lang_start::{{closure}}>",0.0,inferno-collapse-dtrace.exe 12 | inferno_collapse_dtrace::main,0.0,inferno-collapse-dtrace.exe 13 | "inferno::collapse::Collapse::collapse_file",0.0,inferno-collapse-dtrace.exe 14 | "inferno::collapse::dtrace::{{impl}}::collapse,std::io::stdio::StdoutLock>",0.0,inferno-collapse-dtrace.exe 15 | inferno::collapse::dtrace::Folder::on_stack_line,0.0,inferno-collapse-dtrace.exe 16 | alloc::string::{{impl}}::to_string,0.0,inferno-collapse-dtrace.exe 17 | alloc::str::{{impl}}::to_owned,0.0,inferno-collapse-dtrace.exe 18 | alloc::slice::{{impl}}::to_owned,0.0,inferno-collapse-dtrace.exe 19 | alloc::slice::{{impl}}::to_vec,0.0,inferno-collapse-dtrace.exe 20 | alloc::slice::hack::to_vec,0.0,inferno-collapse-dtrace.exe 21 | alloc::vec::Vec::extend_from_slice,0.0,inferno-collapse-dtrace.exe 22 | core::slice::{{impl}}::iter,0.0,inferno-collapse-dtrace.exe 23 | core::ptr::{{impl}}::is_null,0.016612,inferno-collapse-dtrace.exe 24 | alloc::vec::Vec::with_capacity,0.0,inferno-collapse-dtrace.exe 25 | "alloc::raw_vec::RawVec::with_capacity",0.0,inferno-collapse-dtrace.exe 26 | "alloc::raw_vec::RawVec::allocate_in",0.0,inferno-collapse-dtrace.exe 27 | alloc::alloc::{{impl}}::alloc,0.0,inferno-collapse-dtrace.exe 28 | alloc::alloc::alloc,0.0,inferno-collapse-dtrace.exe 29 | func@0x180019140,0.014938,ntdll.dll 30 | inferno::collapse::dtrace::Folder::remove_offset,0.0,inferno-collapse-dtrace.exe 31 | core::iter::range::{{impl}}::next,0.014942,inferno-collapse-dtrace.exe 32 | core::iter::range::{{impl}}::add_usize,0.0,inferno-collapse-dtrace.exe 33 | core::num::{{impl}}::checked_add,0.0,inferno-collapse-dtrace.exe 34 | core::num::{{impl}}::overflowing_add,0.015414,inferno-collapse-dtrace.exe 35 | alloc::string::String::clear,0.0,inferno-collapse-dtrace.exe 36 | alloc::vec::Vec::clear,0.0,inferno-collapse-dtrace.exe 37 | alloc::vec::Vec::truncate,0.030580,inferno-collapse-dtrace.exe 38 | core::iter::range::{{impl}}::next,0.0,inferno-collapse-dtrace.exe 39 | core::iter::range::{{impl}}::add_usize,0.0,inferno-collapse-dtrace.exe 40 | "core::convert::{{impl}}::try_from",0.0,inferno-collapse-dtrace.exe 41 | "core::convert::{{impl}}::into",0.015309,inferno-collapse-dtrace.exe 42 | inferno::collapse::dtrace::Folder::on_stack_end,0.0,inferno-collapse-dtrace.exe 43 | alloc::collections::vec_deque::VecDeque::clear,0.0,inferno-collapse-dtrace.exe 44 | core::ptr::real_drop_in_place>,0.0,inferno-collapse-dtrace.exe 45 | alloc::collections::vec_deque::{{impl}}::drop,0.0,inferno-collapse-dtrace.exe 46 | "core::iter::traits::iterator::Iterator::for_each*,fn(alloc::string::String)>",0.016777,inferno-collapse-dtrace.exe 47 | alloc::string::{{impl}}::deref,0.0,inferno-collapse-dtrace.exe 48 | alloc::vec::{{impl}}::deref,0.0,inferno-collapse-dtrace.exe 49 | core::ptr::{{impl}}::is_null,0.015252,inferno-collapse-dtrace.exe 50 | inferno::collapse::dtrace::Folder::finish,0.0,inferno-collapse-dtrace.exe 51 | std::io::Write::write_fmt,0.0,inferno-collapse-dtrace.exe 52 | core::fmt::write,0.0,inferno-collapse-dtrace.exe 53 | std::io::Write::write_fmt::{{impl}}::write_str,0.0,inferno-collapse-dtrace.exe 54 | std::io::Write::write_all,0.0,inferno-collapse-dtrace.exe 55 | std::io::stdio::{{impl}}::write,0.0,inferno-collapse-dtrace.exe 56 | std::io::buffered::{{impl}}::write>,0.0,inferno-collapse-dtrace.exe 57 | std::io::buffered::BufWriter>::flush_buf>,0.0,inferno-collapse-dtrace.exe 58 | std::sys::windows::stdio::write,0.0,inferno-collapse-dtrace.exe 59 | GetConsoleMode,0.0,KERNELBASE.dll 60 | NtDeviceIoControlFile,0.023113,ntdll.dll 61 | WriteConsoleW,0.0,KERNELBASE.dll 62 | func@0x180008ce8,0.0,KERNELBASE.dll 63 | NtDeviceIoControlFile,0.004207,ntdll.dll 64 | memset,0.001428,VCRUNTIME140.dll 65 | -------------------------------------------------------------------------------- /tests/data/collapse-vtune/end-before-header.csv: -------------------------------------------------------------------------------- 1 | war:Column filter is ON. 2 | -------------------------------------------------------------------------------- /tests/data/collapse-vtune/invalid-time-field.csv: -------------------------------------------------------------------------------- 1 | Function Stack,CPU Time:Self,Module 2 | Total,INVALID TIME,[Unknown] 3 | RtlUserThreadStart,0.0,ntdll.dll 4 | BaseThreadInitThunk,0.0,KERNEL32.DLL 5 | _scrt_common_main_seh,0.0,inferno-collapse-dtrace.exe 6 | main,0.0,inferno-collapse-dtrace.exe 7 | "std::rt::lang_start>",0.0,inferno-collapse-dtrace.exe 8 | std::rt::lang_start_internal,0.0,inferno-collapse-dtrace.exe 9 | rust_maybe_catch_panic,0.0,inferno-collapse-dtrace.exe 10 | "std::panicking::try::do_call",0.0,inferno-collapse-dtrace.exe 11 | "std::rt::lang_start::{{closure}}>",0.0,inferno-collapse-dtrace.exe 12 | inferno_collapse_dtrace::main,0.0,inferno-collapse-dtrace.exe 13 | "inferno::collapse::Collapse::collapse_file",0.0,inferno-collapse-dtrace.exe 14 | "inferno::collapse::dtrace::{{impl}}::collapse,std::io::stdio::StdoutLock>",0.0,inferno-collapse-dtrace.exe 15 | inferno::collapse::dtrace::Folder::on_stack_line,0.0,inferno-collapse-dtrace.exe 16 | alloc::string::{{impl}}::to_string,0.0,inferno-collapse-dtrace.exe 17 | alloc::str::{{impl}}::to_owned,0.0,inferno-collapse-dtrace.exe 18 | alloc::slice::{{impl}}::to_owned,0.0,inferno-collapse-dtrace.exe 19 | alloc::slice::{{impl}}::to_vec,0.0,inferno-collapse-dtrace.exe 20 | alloc::slice::hack::to_vec,0.0,inferno-collapse-dtrace.exe 21 | alloc::vec::Vec::extend_from_slice,0.0,inferno-collapse-dtrace.exe 22 | core::slice::{{impl}}::iter,0.0,inferno-collapse-dtrace.exe 23 | core::ptr::{{impl}}::is_null,0.016612,inferno-collapse-dtrace.exe 24 | alloc::vec::Vec::with_capacity,0.0,inferno-collapse-dtrace.exe 25 | "alloc::raw_vec::RawVec::with_capacity",0.0,inferno-collapse-dtrace.exe 26 | "alloc::raw_vec::RawVec::allocate_in",0.0,inferno-collapse-dtrace.exe 27 | alloc::alloc::{{impl}}::alloc,0.0,inferno-collapse-dtrace.exe 28 | alloc::alloc::alloc,0.0,inferno-collapse-dtrace.exe 29 | func@0x180019140,0.014938,ntdll.dll 30 | inferno::collapse::dtrace::Folder::remove_offset,0.0,inferno-collapse-dtrace.exe 31 | core::iter::range::{{impl}}::next,0.014942,inferno-collapse-dtrace.exe 32 | core::iter::range::{{impl}}::add_usize,0.0,inferno-collapse-dtrace.exe 33 | core::num::{{impl}}::checked_add,0.0,inferno-collapse-dtrace.exe 34 | core::num::{{impl}}::overflowing_add,0.015414,inferno-collapse-dtrace.exe 35 | alloc::string::String::clear,0.0,inferno-collapse-dtrace.exe 36 | alloc::vec::Vec::clear,0.0,inferno-collapse-dtrace.exe 37 | alloc::vec::Vec::truncate,0.030580,inferno-collapse-dtrace.exe 38 | core::iter::range::{{impl}}::next,0.0,inferno-collapse-dtrace.exe 39 | core::iter::range::{{impl}}::add_usize,0.0,inferno-collapse-dtrace.exe 40 | "core::convert::{{impl}}::try_from",0.0,inferno-collapse-dtrace.exe 41 | "core::convert::{{impl}}::into",0.015309,inferno-collapse-dtrace.exe 42 | inferno::collapse::dtrace::Folder::on_stack_end,0.0,inferno-collapse-dtrace.exe 43 | alloc::collections::vec_deque::VecDeque::clear,0.0,inferno-collapse-dtrace.exe 44 | core::ptr::real_drop_in_place>,0.0,inferno-collapse-dtrace.exe 45 | alloc::collections::vec_deque::{{impl}}::drop,0.0,inferno-collapse-dtrace.exe 46 | "core::iter::traits::iterator::Iterator::for_each*,fn(alloc::string::String)>",0.016777,inferno-collapse-dtrace.exe 47 | alloc::string::{{impl}}::deref,0.0,inferno-collapse-dtrace.exe 48 | alloc::vec::{{impl}}::deref,0.0,inferno-collapse-dtrace.exe 49 | core::ptr::{{impl}}::is_null,0.015252,inferno-collapse-dtrace.exe 50 | inferno::collapse::dtrace::Folder::finish,0.0,inferno-collapse-dtrace.exe 51 | std::io::Write::write_fmt,0.0,inferno-collapse-dtrace.exe 52 | core::fmt::write,0.0,inferno-collapse-dtrace.exe 53 | std::io::Write::write_fmt::{{impl}}::write_str,0.0,inferno-collapse-dtrace.exe 54 | std::io::Write::write_all,0.0,inferno-collapse-dtrace.exe 55 | std::io::stdio::{{impl}}::write,0.0,inferno-collapse-dtrace.exe 56 | std::io::buffered::{{impl}}::write>,0.0,inferno-collapse-dtrace.exe 57 | std::io::buffered::BufWriter>::flush_buf>,0.0,inferno-collapse-dtrace.exe 58 | std::sys::windows::stdio::write,0.0,inferno-collapse-dtrace.exe 59 | GetConsoleMode,0.0,KERNELBASE.dll 60 | NtDeviceIoControlFile,0.023113,ntdll.dll 61 | WriteConsoleW,0.0,KERNELBASE.dll 62 | func@0x180008ce8,0.0,KERNELBASE.dll 63 | NtDeviceIoControlFile,0.004207,ntdll.dll 64 | memset,0.001428,VCRUNTIME140.dll 65 | -------------------------------------------------------------------------------- /tests/data/collapse-vtune/skipped-indentation.csv: -------------------------------------------------------------------------------- 1 | Function Stack,CPU Time:Self,Module 2 | Total,0.0,[Unknown] 3 | RtlUserThreadStart,0.0,ntdll.dll 4 | BaseThreadInitThunk,0.0,KERNEL32.DLL 5 | _scrt_common_main_seh,0.0,inferno-collapse-dtrace.exe 6 | main,0.0,inferno-collapse-dtrace.exe 7 | "std::rt::lang_start>",0.0,inferno-collapse-dtrace.exe 8 | std::rt::lang_start_internal,0.0,inferno-collapse-dtrace.exe 9 | rust_maybe_catch_panic,0.0,inferno-collapse-dtrace.exe 10 | "std::panicking::try::do_call",0.0,inferno-collapse-dtrace.exe 11 | "std::rt::lang_start::{{closure}}>",0.0,inferno-collapse-dtrace.exe 12 | inferno_collapse_dtrace::main,0.0,inferno-collapse-dtrace.exe 13 | "inferno::collapse::Collapse::collapse_file",0.0,inferno-collapse-dtrace.exe 14 | "inferno::collapse::dtrace::{{impl}}::collapse,std::io::stdio::StdoutLock>",0.0,inferno-collapse-dtrace.exe 15 | inferno::collapse::dtrace::Folder::on_stack_line,0.0,inferno-collapse-dtrace.exe 16 | alloc::string::{{impl}}::to_string,0.0,inferno-collapse-dtrace.exe 17 | alloc::str::{{impl}}::to_owned,0.0,inferno-collapse-dtrace.exe 18 | alloc::slice::{{impl}}::to_owned,0.0,inferno-collapse-dtrace.exe 19 | alloc::slice::{{impl}}::to_vec,0.0,inferno-collapse-dtrace.exe 20 | alloc::slice::hack::to_vec,0.0,inferno-collapse-dtrace.exe 21 | alloc::vec::Vec::extend_from_slice,0.0,inferno-collapse-dtrace.exe 22 | core::slice::{{impl}}::iter,0.0,inferno-collapse-dtrace.exe 23 | core::ptr::{{impl}}::is_null,0.016612,inferno-collapse-dtrace.exe 24 | alloc::vec::Vec::with_capacity,0.0,inferno-collapse-dtrace.exe 25 | "alloc::raw_vec::RawVec::with_capacity",0.0,inferno-collapse-dtrace.exe 26 | "alloc::raw_vec::RawVec::allocate_in",0.0,inferno-collapse-dtrace.exe 27 | alloc::alloc::{{impl}}::alloc,0.0,inferno-collapse-dtrace.exe 28 | alloc::alloc::alloc,0.0,inferno-collapse-dtrace.exe 29 | func@0x180019140,0.014938,ntdll.dll 30 | inferno::collapse::dtrace::Folder::remove_offset,0.0,inferno-collapse-dtrace.exe 31 | core::iter::range::{{impl}}::next,0.014942,inferno-collapse-dtrace.exe 32 | core::iter::range::{{impl}}::add_usize,0.0,inferno-collapse-dtrace.exe 33 | core::num::{{impl}}::checked_add,0.0,inferno-collapse-dtrace.exe 34 | core::num::{{impl}}::overflowing_add,0.015414,inferno-collapse-dtrace.exe 35 | alloc::string::String::clear,0.0,inferno-collapse-dtrace.exe 36 | alloc::vec::Vec::clear,0.0,inferno-collapse-dtrace.exe 37 | alloc::vec::Vec::truncate,0.030580,inferno-collapse-dtrace.exe 38 | core::iter::range::{{impl}}::next,0.0,inferno-collapse-dtrace.exe 39 | core::iter::range::{{impl}}::add_usize,0.0,inferno-collapse-dtrace.exe 40 | "core::convert::{{impl}}::try_from",0.0,inferno-collapse-dtrace.exe 41 | "core::convert::{{impl}}::into",0.015309,inferno-collapse-dtrace.exe 42 | inferno::collapse::dtrace::Folder::on_stack_end,0.0,inferno-collapse-dtrace.exe 43 | alloc::collections::vec_deque::VecDeque::clear,0.0,inferno-collapse-dtrace.exe 44 | core::ptr::real_drop_in_place>,0.0,inferno-collapse-dtrace.exe 45 | alloc::collections::vec_deque::{{impl}}::drop,0.0,inferno-collapse-dtrace.exe 46 | "core::iter::traits::iterator::Iterator::for_each*,fn(alloc::string::String)>",0.016777,inferno-collapse-dtrace.exe 47 | alloc::string::{{impl}}::deref,0.0,inferno-collapse-dtrace.exe 48 | alloc::vec::{{impl}}::deref,0.0,inferno-collapse-dtrace.exe 49 | core::ptr::{{impl}}::is_null,0.015252,inferno-collapse-dtrace.exe 50 | inferno::collapse::dtrace::Folder::finish,0.0,inferno-collapse-dtrace.exe 51 | std::io::Write::write_fmt,0.0,inferno-collapse-dtrace.exe 52 | core::fmt::write,0.0,inferno-collapse-dtrace.exe 53 | std::io::Write::write_fmt::{{impl}}::write_str,0.0,inferno-collapse-dtrace.exe 54 | std::io::Write::write_all,0.0,inferno-collapse-dtrace.exe 55 | std::io::stdio::{{impl}}::write,0.0,inferno-collapse-dtrace.exe 56 | std::io::buffered::{{impl}}::write>,0.0,inferno-collapse-dtrace.exe 57 | std::io::buffered::BufWriter>::flush_buf>,0.0,inferno-collapse-dtrace.exe 58 | std::sys::windows::stdio::write,0.0,inferno-collapse-dtrace.exe 59 | GetConsoleMode,0.0,KERNELBASE.dll 60 | NtDeviceIoControlFile,0.023113,ntdll.dll 61 | WriteConsoleW,0.0,KERNELBASE.dll 62 | func@0x180008ce8,0.0,KERNELBASE.dll 63 | NtDeviceIoControlFile,0.004207,ntdll.dll 64 | memset,0.001428,VCRUNTIME140.dll 65 | -------------------------------------------------------------------------------- /tests/data/collapse-vtune/vtune.csv: -------------------------------------------------------------------------------- 1 | Function Stack,CPU Time:Self,Module 2 | Total,0.0,[Unknown] 3 | RtlUserThreadStart,0.0,ntdll.dll 4 | BaseThreadInitThunk,0.0,KERNEL32.DLL 5 | _scrt_common_main_seh,0.0,inferno-collapse-dtrace.exe 6 | main,0.0,inferno-collapse-dtrace.exe 7 | "std::rt::lang_start>",0.0,inferno-collapse-dtrace.exe 8 | std::rt::lang_start_internal,0.0,inferno-collapse-dtrace.exe 9 | rust_maybe_catch_panic,0.0,inferno-collapse-dtrace.exe 10 | "std::panicking::try::do_call",0.0,inferno-collapse-dtrace.exe 11 | "std::rt::lang_start::{{closure}}>",0.0,inferno-collapse-dtrace.exe 12 | inferno_collapse_dtrace::main,0.0,inferno-collapse-dtrace.exe 13 | "inferno::collapse::Collapse::collapse_file",0.0,inferno-collapse-dtrace.exe 14 | "inferno::collapse::dtrace::{{impl}}::collapse,std::io::stdio::StdoutLock>",0.0,inferno-collapse-dtrace.exe 15 | inferno::collapse::dtrace::Folder::on_stack_line,0.0,inferno-collapse-dtrace.exe 16 | alloc::string::{{impl}}::to_string,0.0,inferno-collapse-dtrace.exe 17 | alloc::str::{{impl}}::to_owned,0.0,inferno-collapse-dtrace.exe 18 | alloc::slice::{{impl}}::to_owned,0.0,inferno-collapse-dtrace.exe 19 | alloc::slice::{{impl}}::to_vec,0.0,inferno-collapse-dtrace.exe 20 | alloc::slice::hack::to_vec,0.0,inferno-collapse-dtrace.exe 21 | alloc::vec::Vec::extend_from_slice,0.0,inferno-collapse-dtrace.exe 22 | core::slice::{{impl}}::iter,0.0,inferno-collapse-dtrace.exe 23 | core::ptr::{{impl}}::is_null,0.016612,inferno-collapse-dtrace.exe 24 | alloc::vec::Vec::with_capacity,0.0,inferno-collapse-dtrace.exe 25 | "alloc::raw_vec::RawVec::with_capacity",0.0,inferno-collapse-dtrace.exe 26 | "alloc::raw_vec::RawVec::allocate_in",0.0,inferno-collapse-dtrace.exe 27 | alloc::alloc::{{impl}}::alloc,0.0,inferno-collapse-dtrace.exe 28 | alloc::alloc::alloc,0.0,inferno-collapse-dtrace.exe 29 | func@0x180019140,0.014938,ntdll.dll 30 | inferno::collapse::dtrace::Folder::remove_offset,0.0,inferno-collapse-dtrace.exe 31 | core::iter::range::{{impl}}::next,0.014942,inferno-collapse-dtrace.exe 32 | core::iter::range::{{impl}}::add_usize,0.0,inferno-collapse-dtrace.exe 33 | core::num::{{impl}}::checked_add,0.0,inferno-collapse-dtrace.exe 34 | core::num::{{impl}}::overflowing_add,0.015414,inferno-collapse-dtrace.exe 35 | alloc::string::String::clear,0.0,inferno-collapse-dtrace.exe 36 | alloc::vec::Vec::clear,0.0,inferno-collapse-dtrace.exe 37 | alloc::vec::Vec::truncate,0.030580,inferno-collapse-dtrace.exe 38 | core::iter::range::{{impl}}::next,0.0,inferno-collapse-dtrace.exe 39 | core::iter::range::{{impl}}::add_usize,0.0,inferno-collapse-dtrace.exe 40 | "core::convert::{{impl}}::try_from",0.0,inferno-collapse-dtrace.exe 41 | "core::convert::{{impl}}::into",0.015309,inferno-collapse-dtrace.exe 42 | inferno::collapse::dtrace::Folder::on_stack_end,0.0,inferno-collapse-dtrace.exe 43 | alloc::collections::vec_deque::VecDeque::clear,0.0,inferno-collapse-dtrace.exe 44 | core::ptr::real_drop_in_place>,0.0,inferno-collapse-dtrace.exe 45 | alloc::collections::vec_deque::{{impl}}::drop,0.0,inferno-collapse-dtrace.exe 46 | "core::iter::traits::iterator::Iterator::for_each*,fn(alloc::string::String)>",0.016777,inferno-collapse-dtrace.exe 47 | alloc::string::{{impl}}::deref,0.0,inferno-collapse-dtrace.exe 48 | alloc::vec::{{impl}}::deref,0.0,inferno-collapse-dtrace.exe 49 | core::ptr::{{impl}}::is_null,0.015252,inferno-collapse-dtrace.exe 50 | inferno::collapse::dtrace::Folder::finish,0.0,inferno-collapse-dtrace.exe 51 | std::io::Write::write_fmt,0.0,inferno-collapse-dtrace.exe 52 | core::fmt::write,0.0,inferno-collapse-dtrace.exe 53 | std::io::Write::write_fmt::{{impl}}::write_str,0.0,inferno-collapse-dtrace.exe 54 | std::io::Write::write_all,0.0,inferno-collapse-dtrace.exe 55 | std::io::stdio::{{impl}}::write,0.0,inferno-collapse-dtrace.exe 56 | std::io::buffered::{{impl}}::write>,0.0,inferno-collapse-dtrace.exe 57 | std::io::buffered::BufWriter>::flush_buf>,0.0,inferno-collapse-dtrace.exe 58 | std::sys::windows::stdio::write,0.0,inferno-collapse-dtrace.exe 59 | GetConsoleMode,0.0,KERNELBASE.dll 60 | NtDeviceIoControlFile,0.023113,ntdll.dll 61 | WriteConsoleW,0.0,KERNELBASE.dll 62 | func@0x180008ce8,0.0,KERNELBASE.dll 63 | NtDeviceIoControlFile,0.004207,ntdll.dll 64 | memset,0.001428,VCRUNTIME140.dll 65 | -------------------------------------------------------------------------------- /tests/data/collapse-xctrace/results/basic.folded: -------------------------------------------------------------------------------- 1 | start;0x18d3df0f1 1 2 | start;dyld4::prepare(dyld4::APIs&, dyld3::MachOAnalyzer const*) 1 3 | start;main;std::rt::lang_start_internal;std::rt::lang_start::_{{closure}};std::sys_common::backtrace::__rust_begin_short_backtrace;core::cmp::impls::_::lt 53 4 | start;main;std::rt::lang_start_internal;std::rt::lang_start::_{{closure}};std::sys_common::backtrace::__rust_begin_short_backtrace;rust_test2::main 1490 5 | start;main;std::rt::lang_start_internal;std::rt::lang_start::_{{closure}};std::sys_common::backtrace::__rust_begin_short_backtrace;rust_test2::main; as core::iter::range::RangeIteratorImpl>::spec_next 1274 6 | start;main;std::rt::lang_start_internal;std::rt::lang_start::_{{closure}};std::sys_common::backtrace::__rust_begin_short_backtrace;rust_test2::main;rust_test2::bar 3548 7 | start;main;std::rt::lang_start_internal;std::rt::lang_start::_{{closure}};std::sys_common::backtrace::__rust_begin_short_backtrace;rust_test2::main;rust_test2::foo 3214 8 | -------------------------------------------------------------------------------- /tests/data/collapse-xctrace/results/simple_frame_without_binary_info.folded: -------------------------------------------------------------------------------- 1 | 0x104745730;0x1047b15cd 1 2 | start;dyld4::prepare(dyld4::APIs&, dyld3::MachOAnalyzer const*) 1 3 | start;dyld4::prepare(dyld4::APIs&, dyld3::MachOAnalyzer const*);dyld4::APIs::runAllInitializersForMain();dyld4::RuntimeState::notifyObjCInit(dyld4::Loader const*);__kdebug_trace64 1 4 | start;main;std::rt::lang_start_internal;std::rt::lang_start::_{{closure}};std::sys_common::backtrace::__rust_begin_short_backtrace;core::cmp::impls::_::lt 55 5 | start;main;std::rt::lang_start_internal;std::rt::lang_start::_{{closure}};std::sys_common::backtrace::__rust_begin_short_backtrace;rust_test2::main 1511 6 | start;main;std::rt::lang_start_internal;std::rt::lang_start::_{{closure}};std::sys_common::backtrace::__rust_begin_short_backtrace;rust_test2::main; as core::iter::range::RangeIteratorImpl>::spec_next 1347 7 | start;main;std::rt::lang_start_internal;std::rt::lang_start::_{{closure}};std::sys_common::backtrace::__rust_begin_short_backtrace;rust_test2::main;rust_test2::bar 3519 8 | start;main;std::rt::lang_start_internal;std::rt::lang_start::_{{closure}};std::sys_common::backtrace::__rust_begin_short_backtrace;rust_test2::main;rust_test2::foo 3281 9 | -------------------------------------------------------------------------------- /tests/data/diff-folded/after.txt: -------------------------------------------------------------------------------- 1 | dd;[unknown];[dd] 7 2 | dd;[unknown];read 13 3 | dd;[unknown];read;system_call_[k];__fdget_pos_[k] 15 4 | dd;[unknown];read;system_call_[k];sys_read_[k];vfs_read_[k];fsnotify_[k] 4 5 | dd;[unknown];write;system_call_[k];sys_write_[k];vfs_write_[k];fsnotify_[k];__srcu_read_unlock_[k] 15 6 | dd;[unknown];write;system_call_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k] 8 7 | dd;write;system_call_[k];sys_write_[k];__fdget_pos_[k];__fdget_[k];__fget_light_[k] 12 8 | dd;write;system_call_[k];sys_write_[k];vfs_write_[k];fsnotify_[k];__srcu_read_unlock_[k] 6 9 | -------------------------------------------------------------------------------- /tests/data/diff-folded/bad_before.txt: -------------------------------------------------------------------------------- 1 | dd;[unknown];[dd] 3 2 | dd;[unknown];read 14 3 | dd;[unknown];read;system_call_[k];__fdget_pos_[k] 4 | dd;[unknown];read;system_call_[k];sys_read_[k];vfs_read_[k];fsnotify_[k] 6 5 | dd;[unknown];0x234f2abc;system_call_[k];0xF1BDE348 1 6 | dd;[unknown];write;system_call_[k];sys_write_[k];vfs_write_[k];fsnotify_[k];__srcu_read_unlock_[k] 20 7 | dd;[unknown];write;system_call_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k] 3 8 | dd;write;system_call_[k];sys_write_[k];vfs_write_[k];fsnotify_[k];__srcu_read_unlock_[k] 7 9 | -------------------------------------------------------------------------------- /tests/data/diff-folded/before.txt: -------------------------------------------------------------------------------- 1 | dd;[unknown];[dd] 3 2 | dd;[unknown];read 14 3 | dd;[unknown];read;system_call_[k];__fdget_pos_[k] 11 4 | dd;[unknown];read;system_call_[k];sys_read_[k];vfs_read_[k];fsnotify_[k] 6 5 | dd;[unknown];0x234f2abc;system_call_[k];0xF1BDE348 1 6 | dd;[unknown];write;system_call_[k];sys_write_[k];vfs_write_[k];fsnotify_[k];__srcu_read_unlock_[k] 20 7 | dd;[unknown];write;system_call_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k] 3 8 | dd;write;system_call_[k];sys_write_[k];vfs_write_[k];fsnotify_[k];__srcu_read_unlock_[k] 7 9 | -------------------------------------------------------------------------------- /tests/data/diff-folded/before_fractionals.txt: -------------------------------------------------------------------------------- 1 | dd;[unknown];[dd] 3 2 | dd;[unknown];read 14.2343983248722834234 3 | dd;[unknown];read;system_call_[k];__fdget_pos_[k] 11.98 4 | dd;[unknown];read;system_call_[k];sys_read_[k];vfs_read_[k];fsnotify_[k] 6.2 5 | dd;[unknown];0x234f2abc;system_call_[k];0xF1BDE348 1.0 6 | dd;[unknown];write;system_call_[k];sys_write_[k];vfs_write_[k];fsnotify_[k];__srcu_read_unlock_[k] 20. 7 | dd;[unknown];write;system_call_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k] 3.11 8 | dd;write;system_call_[k];sys_write_[k];vfs_write_[k];fsnotify_[k];__srcu_read_unlock_[k] 7.56 9 | -------------------------------------------------------------------------------- /tests/data/diff-folded/results/default.txt: -------------------------------------------------------------------------------- 1 | dd;[unknown];read;system_call_[k];sys_read_[k];vfs_read_[k];fsnotify_[k] 6 4 2 | dd;write;system_call_[k];sys_write_[k];__fdget_pos_[k];__fdget_[k];__fget_light_[k] 0 12 3 | dd;[unknown];read;system_call_[k];__fdget_pos_[k] 11 15 4 | dd;[unknown];write;system_call_[k];sys_write_[k];vfs_write_[k];fsnotify_[k];__srcu_read_unlock_[k] 20 15 5 | dd;[unknown];[dd] 3 7 6 | dd;[unknown];read 14 13 7 | dd;[unknown];write;system_call_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k] 3 8 8 | dd;write;system_call_[k];sys_write_[k];vfs_write_[k];fsnotify_[k];__srcu_read_unlock_[k] 7 6 9 | dd;[unknown];0x234f2abc;system_call_[k];0xF1BDE348 1 0 10 | -------------------------------------------------------------------------------- /tests/data/diff-folded/results/fractionals.txt: -------------------------------------------------------------------------------- 1 | dd;write;system_call_[k];sys_write_[k];vfs_write_[k];fsnotify_[k];__srcu_read_unlock_[k] 7 6 2 | dd;[unknown];read;system_call_[k];sys_read_[k];vfs_read_[k];fsnotify_[k] 6 4 3 | dd;[unknown];read;system_call_[k];__fdget_pos_[k] 11 15 4 | dd;[unknown];0x234f2abc;system_call_[k];0xF1BDE348 1 0 5 | dd;[unknown];read 14 13 6 | dd;[unknown];write;system_call_[k];sys_write_[k];vfs_write_[k];fsnotify_[k];__srcu_read_unlock_[k] 20 15 7 | dd;[unknown];write;system_call_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k] 3 8 8 | dd;[unknown];[dd] 3 7 9 | dd;write;system_call_[k];sys_write_[k];__fdget_pos_[k];__fdget_[k];__fget_light_[k] 0 12 10 | -------------------------------------------------------------------------------- /tests/data/diff-folded/results/normalize.txt: -------------------------------------------------------------------------------- 1 | dd;[unknown];read;system_call_[k];sys_read_[k];vfs_read_[k];fsnotify_[k] 7 4 2 | dd;[unknown];[dd] 3 7 3 | dd;[unknown];read 17 13 4 | dd;write;system_call_[k];sys_write_[k];vfs_write_[k];fsnotify_[k];__srcu_read_unlock_[k] 8 6 5 | dd;write;system_call_[k];sys_write_[k];__fdget_pos_[k];__fdget_[k];__fget_light_[k] 0 12 6 | dd;[unknown];write;system_call_[k];sys_write_[k];vfs_write_[k];fsnotify_[k];__srcu_read_unlock_[k] 24 15 7 | dd;[unknown];write;system_call_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k] 3 8 8 | dd;[unknown];0x234f2abc;system_call_[k];0xF1BDE348 1 0 9 | dd;[unknown];read;system_call_[k];__fdget_pos_[k] 13 15 10 | -------------------------------------------------------------------------------- /tests/data/diff-folded/results/strip_hex.txt: -------------------------------------------------------------------------------- 1 | dd;[unknown];write;system_call_[k];sys_write_[k];vfs_write_[k];rw_verify_area_[k] 3 8 2 | dd;[unknown];read 14 13 3 | dd;[unknown];write;system_call_[k];sys_write_[k];vfs_write_[k];fsnotify_[k];__srcu_read_unlock_[k] 20 15 4 | dd;[unknown];read;system_call_[k];sys_read_[k];vfs_read_[k];fsnotify_[k] 6 4 5 | dd;[unknown];read;system_call_[k];__fdget_pos_[k] 11 15 6 | dd;write;system_call_[k];sys_write_[k];__fdget_pos_[k];__fdget_[k];__fget_light_[k] 0 12 7 | dd;[unknown];[dd] 3 7 8 | dd;write;system_call_[k];sys_write_[k];vfs_write_[k];fsnotify_[k];__srcu_read_unlock_[k] 7 6 9 | dd;[unknown];0x...;system_call_[k];0x... 1 0 10 | -------------------------------------------------------------------------------- /tests/data/flamegraph/austin/flames.txt: -------------------------------------------------------------------------------- 1 | # austin: 3.3.0 2 | # interval: 100 3 | # mode: wall 4 | 5 | P6360;T6360;:_install_external_importers:1187;:_find_and_load:1007;:_find_and_load_unlocked:986;:_load_unlocked:680;:exec_module:838;::35;:_find_and_load:1007;:_find_and_load_unlocked:986;:_load_unlocked:680;:exec_module:768;:_call_with_frames_removed:228 105 6 | P6360;T6360;:_install_external_importers:1187;:_find_and_load:1007;:_find_and_load_unlocked:986;:_load_unlocked:680;:exec_module:838;::828 186 7 | 8 | # duration: 23873 9 | -------------------------------------------------------------------------------- /tests/data/flamegraph/bad-lines/bad-lines.txt: -------------------------------------------------------------------------------- 1 | cksum;_start;__libc_start_main;main;cksum 31 2 | cksum;cksum 6 3 | THIS IS A BAD LINE 4 | cksum;cksum;__GI___fread_unlocked;_IO_file_xsgetn;_IO_file_read;entry_SYSCALL_64_fastpath_[k];sys_read_[k];vfs_read_[k];__vfs_read_[k];ext4_file_read_iter_[k] 1 5 | cksum;main;cksum 19 6 | THIS IS A BAD FRACTIONAL LINE 12V.43 7 | noploop;[unknown] 2 8 | noploop;main 274 9 | 10 | noploop;main 274 11 | -------------------------------------------------------------------------------- /tests/data/flamegraph/differential/perf-cycles-instructions-01-collapsed-all-diff.txt: -------------------------------------------------------------------------------- 1 | cksum;_start;__libc_start_main;main;cksum 31 56 2 | cksum;cksum 6 2 3 | cksum;cksum;__GI___fread_unlocked;_IO_file_xsgetn;_IO_file_read;entry_SYSCALL_64_fastpath_[k];sys_read_[k];vfs_read_[k];__vfs_read_[k];ext4_file_read_iter_[k] 1 3 4 | cksum;main;cksum 19 35 5 | noploop;[unknown] 2 2 6 | noploop;main 274 415 7 | -------------------------------------------------------------------------------- /tests/data/flamegraph/empty/empty.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ERROR: No valid input provided to flamegraph 5 | -------------------------------------------------------------------------------- /tests/data/flamegraph/empty/empty.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonhoo/inferno/5ee7d6bb68ac9627010ef468c81f4f264cd0d399/tests/data/flamegraph/empty/empty.txt -------------------------------------------------------------------------------- /tests/data/flamegraph/fractional-samples/fractional.txt: -------------------------------------------------------------------------------- 1 | cksum;_start;__libc_start_main;main;cksum 31.23 2 | cksum;cksum 6.1 3 | cksum;cksum;__GI___fread_unlocked;_IO_file_xsgetn;_IO_file_read;entry_SYSCALL_64_fastpath_[k];sys_read_[k];vfs_read_[k];__vfs_read_[k];ext4_file_read_iter_[k] 1.4 4 | cksum;main;cksum 19.0 5 | noploop;[unknown] 2.567 6 | noploop;main 274.321 7 | -------------------------------------------------------------------------------- /tests/data/flamegraph/fractional-samples/no-fractionals.txt: -------------------------------------------------------------------------------- 1 | cksum;_start;__libc_start_main;main;cksum 31 2 | cksum;cksum 6 3 | cksum;cksum;__GI___fread_unlocked;_IO_file_xsgetn;_IO_file_read;entry_SYSCALL_64_fastpath_[k];sys_read_[k];vfs_read_[k];__vfs_read_[k];ext4_file_read_iter_[k] 1 4 | cksum;main;cksum 19 5 | noploop;[unknown] 2 6 | noploop;main 274 7 | -------------------------------------------------------------------------------- /tests/data/flamegraph/fractional-samples/tricky-stack.txt: -------------------------------------------------------------------------------- 1 | rustc; as alloc::vec::SpecExtend>::from_iter;memcpy@GLIBC_2.2.5 3 2 | -------------------------------------------------------------------------------- /tests/data/flamegraph/fractional-samples/with-space.txt: -------------------------------------------------------------------------------- 1 | cksum;_start;__libc_start_main;main;cksum 31.23 2 | cksum;cksum 6.1 3 | cksum;cksum;__GI___fread_unlocked;_IO_file_xsgetn;_IO_file_read;entry_SYSCALL_64_fastpath_[k];sys_read_[k];vfs_read_[k];__vfs_read_[k];ext4_file_read_iter_[k] 1.4 4 | cksum;main;core::array::::default::h67c9877e6f2a615c 19.0 5 | noploop;[unknown] 2.567 6 | noploop;main 274.321 7 | -------------------------------------------------------------------------------- /tests/data/flamegraph/fractional-samples/zero-fractionals.txt: -------------------------------------------------------------------------------- 1 | cksum;_start;__libc_start_main;main;cksum 31.0 2 | cksum;cksum 6. 3 | cksum;cksum;__GI___fread_unlocked;_IO_file_xsgetn;_IO_file_read;entry_SYSCALL_64_fastpath_[k];sys_read_[k];vfs_read_[k];__vfs_read_[k];ext4_file_read_iter_[k] 1 4 | cksum;main;cksum 19 5 | noploop;[unknown] 2.0 6 | noploop;main 274 7 | -------------------------------------------------------------------------------- /tests/data/flamegraph/grey-frames/grey-frames.txt: -------------------------------------------------------------------------------- 1 | cksum;- 6 2 | cksum;_start;__libc_start_main;main;-- 31 3 | cksum;cksum;__GI___fread_unlocked;_IO_file_xsgetn;_IO_file_read;entry_SYSCALL_64_fastpath_[k];sys_read_[k];vfs_read_[k];__vfs_read_[k];ext4_file_read_iter_[k] 1 4 | cksum;main;cksum 19 5 | noploop;[unknown] 2 6 | noploop;main 274 7 | -------------------------------------------------------------------------------- /tests/data/flamegraph/nameattr/nameattr.txt: -------------------------------------------------------------------------------- 1 | main class=inferno title=foo g_extra=stroke-width="1" stroke="green" href=https://github.com/jonhoo/inferno target=_blank a_extra=rel="external" hreflang="en-us" 2 | cksum title=bar class=flames href=https://github.com/jonhoo/inferno 3 | noploop id=test 4 | -------------------------------------------------------------------------------- /tests/data/flamegraph/nameattr/nameattr_duplicate_attributes.txt: -------------------------------------------------------------------------------- 1 | main class=inferno title=foo g_extra=stroke-width="1" stroke="green" href=https://github.com/jonhoo/inferno class=inferno2 target=_blank a_extra=rel="external" hreflang="en-us" 2 | cksum title=bar class=flames 3 | noploop id=test id=test2 g_extra=id="test3" 4 | -------------------------------------------------------------------------------- /tests/data/flamegraph/nameattr/nameattr_empty_attribute.txt: -------------------------------------------------------------------------------- 1 | main class=inferno title=foo g_extra=stroke-width="1" stroke="green" href=https://github.com/jonhoo/inferno target=_blank a_extra=rel="external" hreflang="en-us" 2 | cksum title=bar class=flames href=https://github.com/jonhoo/inferno 3 | noploop id=test 4 | -------------------------------------------------------------------------------- /tests/data/flamegraph/nameattr/nameattr_empty_first_line.txt: -------------------------------------------------------------------------------- 1 | 2 | main class=inferno title=foo g_extra=stroke-width="1" stroke="green" href=https://github.com/jonhoo/inferno target=_blank a_extra=rel="external" hreflang="en-us" 3 | cksum title=bar class=flames href=https://github.com/jonhoo/inferno 4 | noploop id=test 5 | -------------------------------------------------------------------------------- /tests/data/flamegraph/nameattr/nameattr_invalid_attribute.txt: -------------------------------------------------------------------------------- 1 | main class=inferno invalid=font-weight: bold; title=foo g_extra=stroke-width="1" stroke="green" href=https://github.com/jonhoo/inferno target=_blank a_extra=rel="external" hreflang="en-us" 2 | cksum title=bar class=flames href=https://github.com/jonhoo/inferno 3 | noploop id=test 4 | -------------------------------------------------------------------------------- /tests/data/flamegraph/narrow-blocks/narrow-blocks.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 24 | 34 | 35 | Flame Graph 36 | 37 | Reset Zoom 38 | Search 39 | 40 | 41 | 42 | _start (31,000 samples, 8.03%) 43 | 44 | _start 45 | 46 | 47 | __libc_start_main (31,000 samples, 8.03%) 48 | 49 | __libc_star.. 50 | 51 | 52 | main (31,000 samples, 8.03%) 53 | 54 | main 55 | 56 | 57 | cksum (31,000 samples, 8.03%) 58 | 59 | cksum 60 | 61 | 62 | cksum (60,001 samples, 15.54%) 63 | 64 | cksum 65 | 66 | 67 | cksum (110,001 samples, 28.50%) 68 | 69 | cksum 70 | 71 | 72 | main (19,000 samples, 4.92%) 73 | 74 | main 75 | 76 | 77 | cksum (19,000 samples, 4.92%) 78 | 79 | cksum 80 | 81 | 82 | [unknown] (2,000 samples, 0.52%) 83 | 84 | 85 | 86 | 87 | all (386,001 samples, 100%) 88 | 89 | 90 | 91 | 92 | noploop (276,000 samples, 71.50%) 93 | 94 | noploop 95 | 96 | 97 | main (274,000 samples, 70.98%) 98 | 99 | main 100 | 101 | 102 | -------------------------------------------------------------------------------- /tests/data/flamegraph/narrow-blocks/narrow-blocks.txt: -------------------------------------------------------------------------------- 1 | cksum;_start;__libc_start_main;main;cksum 31000 2 | cksum;cksum 60000 3 | cksum;cksum;__GI___fread_unlocked;_IO_file_xsgetn;_IO_file_read;entry_SYSCALL_64_fastpath_[k];sys_read_[k];vfs_read_[k];__vfs_read_[k];ext4_file_read_iter_[k] 1 4 | cksum;main;cksum 19000 5 | noploop;[unknown] 2000 6 | noploop;main 274000 7 | -------------------------------------------------------------------------------- /tests/data/flamegraph/palette-map/palette_invalid.map: -------------------------------------------------------------------------------- 1 | ->rgb(255,230,55) 2 | ClassLoaderData::oops_do->invalid(244,125,43) 3 | ClassLoaderDataGraph::oops_do-----rgb(244,125,43) 4 | GCTaskThread::run->rgb(243,216) 5 | InstanceKlass::oop_push_contents-> 6 | Interpreter_[j]-rgb(237,180,35) 7 | JavaCalls::call_helper->rgb(243,120,41) 8 | JavaCalls::call_virtual->rgb(224,120,21) 9 | JavaThread::run->rgb(243,120,41) 10 | --------------------------------------------------------------------------------