Full Code of google/pprof for AI

main a15ffb7f9dcc cached
239 files
1.1 MB
385.2k tokens
1120 symbols
1 requests
Download .txt
Showing preview only (1,224K chars total). Download the full file or copy to clipboard to get everything.
Repository: google/pprof
Branch: main
Commit: a15ffb7f9dcc
Files: 239
Total size: 1.1 MB

Directory structure:
gitextract_qb_j6arz/

├── .gitattributes
├── .github/
│   ├── ISSUE_TEMPLATE.md
│   ├── dependabot.yml
│   └── workflows/
│       └── ci.yaml
├── .gitignore
├── AUTHORS
├── CONTRIBUTING.md
├── CONTRIBUTORS
├── LICENSE
├── README.md
├── browsertests/
│   ├── README.md
│   ├── browser_test.go
│   ├── go.mod
│   ├── go.sum
│   ├── testdata/
│   │   ├── testfixture.js
│   │   └── testflame.js
│   └── testutils.go
├── doc/
│   └── README.md
├── driver/
│   └── driver.go
├── fuzz/
│   ├── README.md
│   ├── corpus/
│   │   ├── cppbench.cpu.pb
│   │   ├── empty
│   │   ├── go.crc32.cpu.pb
│   │   ├── gobench.cpu.pb
│   │   └── java.cpu.pb
│   ├── fuzz_test.go
│   ├── main.go
│   └── testdata/
│       └── 7e3c92482f6f39fc502b822ded792c589849cca8
├── go.mod
├── go.sum
├── internal/
│   ├── binutils/
│   │   ├── addr2liner.go
│   │   ├── addr2liner_llvm.go
│   │   ├── addr2liner_nm.go
│   │   ├── binutils.go
│   │   ├── binutils_test.go
│   │   ├── disasm.go
│   │   ├── disasm_test.go
│   │   └── testdata/
│   │       ├── build_binaries.go
│   │       ├── exe_linux_64
│   │       ├── exe_mac_64
│   │       ├── exe_mac_64.dSYM/
│   │       │   └── Contents/
│   │       │       ├── Info.plist
│   │       │       └── Resources/
│   │       │           └── DWARF/
│   │       │               └── exe_mac_64
│   │       ├── fake-llvm-symbolizer
│   │       ├── hello.c
│   │       ├── lib.c
│   │       ├── lib_mac_64
│   │       ├── lib_mac_64.dSYM/
│   │       │   └── Contents/
│   │       │       ├── Info.plist
│   │       │       └── Resources/
│   │       │           └── DWARF/
│   │       │               └── lib_mac_64
│   │       ├── malformed_elf
│   │       └── malformed_macho
│   ├── driver/
│   │   ├── cli.go
│   │   ├── commands.go
│   │   ├── config.go
│   │   ├── driver.go
│   │   ├── driver_focus.go
│   │   ├── driver_test.go
│   │   ├── fetch.go
│   │   ├── fetch_test.go
│   │   ├── flags.go
│   │   ├── html/
│   │   │   ├── common.css
│   │   │   ├── common.js
│   │   │   ├── graph.css
│   │   │   ├── graph.html
│   │   │   ├── header.html
│   │   │   ├── plaintext.html
│   │   │   ├── source.html
│   │   │   ├── stacks.css
│   │   │   ├── stacks.html
│   │   │   ├── stacks.js
│   │   │   └── top.html
│   │   ├── interactive.go
│   │   ├── interactive_test.go
│   │   ├── options.go
│   │   ├── settings.go
│   │   ├── settings_test.go
│   │   ├── stacks.go
│   │   ├── svg.go
│   │   ├── tagroot.go
│   │   ├── tagroot_test.go
│   │   ├── tempfile.go
│   │   ├── tempfile_test.go
│   │   ├── testdata/
│   │   │   ├── cppbench.contention
│   │   │   ├── cppbench.cpu
│   │   │   ├── cppbench.cpu_no_samples_type
│   │   │   ├── cppbench.small.contention
│   │   │   ├── file1000.src
│   │   │   ├── file2000.src
│   │   │   ├── file3000.src
│   │   │   ├── go.crc32.cpu
│   │   │   ├── go.nomappings.crash
│   │   │   ├── pprof.contention.cum.files.dot
│   │   │   ├── pprof.contention.flat.addresses.dot.focus.ignore
│   │   │   ├── pprof.cpu.addresses.traces
│   │   │   ├── pprof.cpu.call_tree.callgrind
│   │   │   ├── pprof.cpu.callgrind
│   │   │   ├── pprof.cpu.comments
│   │   │   ├── pprof.cpu.cum.lines.text.focus.hide
│   │   │   ├── pprof.cpu.cum.lines.text.hide
│   │   │   ├── pprof.cpu.cum.lines.text.show
│   │   │   ├── pprof.cpu.cum.lines.topproto.hide
│   │   │   ├── pprof.cpu.cum.lines.tree.show_from
│   │   │   ├── pprof.cpu.flat.addresses.disasm
│   │   │   ├── pprof.cpu.flat.addresses.noinlines.text
│   │   │   ├── pprof.cpu.flat.addresses.weblist
│   │   │   ├── pprof.cpu.flat.filefunctions.noinlines.text
│   │   │   ├── pprof.cpu.flat.functions.call_tree.dot
│   │   │   ├── pprof.cpu.flat.functions.dot
│   │   │   ├── pprof.cpu.flat.functions.noinlines.text
│   │   │   ├── pprof.cpu.flat.functions.text
│   │   │   ├── pprof.cpu.lines.topproto
│   │   │   ├── pprof.cpu.peek
│   │   │   ├── pprof.cpu.tags
│   │   │   ├── pprof.cpu.tags.focus.ignore
│   │   │   ├── pprof.cpu.traces
│   │   │   ├── pprof.cpusmall.flat.addresses.tree
│   │   │   ├── pprof.heap.callgrind
│   │   │   ├── pprof.heap.comments
│   │   │   ├── pprof.heap.cum.lines.tree.focus
│   │   │   ├── pprof.heap.cum.relative_percentages.tree.focus
│   │   │   ├── pprof.heap.flat.files.seconds.text
│   │   │   ├── pprof.heap.flat.files.text
│   │   │   ├── pprof.heap.flat.files.text.focus
│   │   │   ├── pprof.heap.flat.inuse_objects.text
│   │   │   ├── pprof.heap.flat.inuse_objects.text.all_frames
│   │   │   ├── pprof.heap.flat.inuse_space.dot.focus
│   │   │   ├── pprof.heap.flat.inuse_space.dot.focus.ignore
│   │   │   ├── pprof.heap.flat.lines.dot.focus
│   │   │   ├── pprof.heap.tags
│   │   │   ├── pprof.heap.tags.unit
│   │   │   ├── pprof.heap_alloc.flat.alloc_objects.text
│   │   │   ├── pprof.heap_alloc.flat.alloc_space.dot
│   │   │   ├── pprof.heap_alloc.flat.alloc_space.dot.focus
│   │   │   ├── pprof.heap_alloc.flat.alloc_space.dot.hide
│   │   │   ├── pprof.heap_request.relative_percentages.tags.focus
│   │   │   ├── pprof.heap_request.tags.focus
│   │   │   ├── pprof.heap_sizetags.dot
│   │   │   ├── pprof.heap_tags.traces
│   │   │   ├── pprof.long_name_funcs.dot
│   │   │   ├── pprof.long_name_funcs.text
│   │   │   └── pprof.unknown.flat.functions.call_tree.text
│   │   ├── webhtml.go
│   │   ├── webui.go
│   │   └── webui_test.go
│   ├── elfexec/
│   │   ├── elfexec.go
│   │   └── elfexec_test.go
│   ├── graph/
│   │   ├── dotgraph.go
│   │   ├── dotgraph_test.go
│   │   ├── graph.go
│   │   ├── graph_test.go
│   │   └── testdata/
│   │       ├── compose1.dot
│   │       ├── compose2.dot
│   │       ├── compose3.dot
│   │       ├── compose4.dot
│   │       ├── compose5.dot
│   │       ├── compose6.dot
│   │       ├── compose7.dot
│   │       └── compose9.dot
│   ├── measurement/
│   │   ├── measurement.go
│   │   └── measurement_test.go
│   ├── plugin/
│   │   └── plugin.go
│   ├── proftest/
│   │   ├── BUILD
│   │   ├── proftest.go
│   │   └── testdata/
│   │       └── large.cpu
│   ├── report/
│   │   ├── package.go
│   │   ├── package_test.go
│   │   ├── report.go
│   │   ├── report_test.go
│   │   ├── shortnames.go
│   │   ├── shortnames_test.go
│   │   ├── source.go
│   │   ├── source_html.go
│   │   ├── source_test.go
│   │   ├── stacks.go
│   │   ├── stacks_test.go
│   │   ├── synth.go
│   │   ├── synth_test.go
│   │   └── testdata/
│   │       ├── README.md
│   │       ├── sample/
│   │       │   └── sample.go
│   │       ├── sample.cpu
│   │       ├── source.dot
│   │       ├── source.rpt
│   │       ├── source1
│   │       └── source2
│   ├── symbolizer/
│   │   ├── symbolizer.go
│   │   └── symbolizer_test.go
│   ├── symbolz/
│   │   ├── symbolz.go
│   │   └── symbolz_test.go
│   └── transport/
│       └── transport.go
├── pprof.go
├── profile/
│   ├── encode.go
│   ├── filter.go
│   ├── filter_test.go
│   ├── index.go
│   ├── index_test.go
│   ├── legacy_java_profile.go
│   ├── legacy_profile.go
│   ├── legacy_profile_test.go
│   ├── merge.go
│   ├── merge_test.go
│   ├── profile.go
│   ├── profile_test.go
│   ├── proto.go
│   ├── proto_test.go
│   ├── prune.go
│   ├── prune_test.go
│   └── testdata/
│       ├── cppbench.contention
│       ├── cppbench.contention.string
│       ├── cppbench.cpu
│       ├── cppbench.cpu.string
│       ├── cppbench.growth
│       ├── cppbench.growth.string
│       ├── cppbench.heap
│       ├── cppbench.heap.string
│       ├── cppbench.thread
│       ├── cppbench.thread.all
│       ├── cppbench.thread.all.string
│       ├── cppbench.thread.none
│       ├── cppbench.thread.none.string
│       ├── cppbench.thread.string
│       ├── go.crc32.cpu
│       ├── go.crc32.cpu.string
│       ├── go.godoc.thread
│       ├── go.godoc.thread.string
│       ├── gobench.cpu
│       ├── gobench.cpu.string
│       ├── gobench.heap
│       ├── gobench.heap.string
│       ├── java.contention
│       ├── java.contention.string
│       ├── java.cpu
│       ├── java.cpu.string
│       ├── java.heap
│       └── java.heap.string
├── proto/
│   ├── README.md
│   └── profile.proto
├── test.sh
└── third_party/
    └── svgpan/
        ├── LICENSE
        ├── svgpan.go
        └── svgpan.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitattributes
================================================
* text=auto eol=lf


================================================
FILE: .github/ISSUE_TEMPLATE.md
================================================
Please answer these questions before submitting your issue. Thanks!

### What version of pprof are you using?

If you are using pprof via `go tool pprof`, what's your `go env` output?
If you run pprof from GitHub, what's the Git revision?


### What operating system and processor architecture are you using?


### What did you do?

If possible, provide a recipe for reproducing the error.
Attaching a profile you are trying to analyze is good.


### What did you expect to see?


### What did you see instead?



================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "monthly"


================================================
FILE: .github/workflows/ci.yaml
================================================
name: ci
on:
  push:
    branches:
      - main
  pull_request:
  schedule:
    - cron: '0 2 * * *' # Run every day, at 2AM UTC.
env:
  GOPATH: ${{ github.workspace }}
  WORKING_DIR: ./src/github.com/google/pprof/
jobs:
  test-mac:
    runs-on: ${{ matrix.os }}
    defaults:
      run:
        working-directory: ${{ env.WORKING_DIR }}
    strategy:
      fail-fast: false
      matrix:
        go: ['1.24', '1.25', 'tip']
        # Supported macOS versions can be found in
        # https://github.com/actions/virtual-environments#available-environments.
        os: ['macos-14', 'macos-15']
        # Supported Xcode versions can be found in:
        # - https://github.com/actions/virtual-environments/blob/main/images/macos/macos-14-Readme.md#xcode
        # - https://github.com/actions/virtual-environments/blob/main/images/macos/macos-15-Readme.md#xcode
        xcode-version: ['26.0', '16.4', '16.3', '16.2', '16.1', '16.0', '15.4', '15.3', '15.2', '15.1', '15.0.1']
        exclude:
          - os: 'macos-14'
            xcode-version: '26.0'
          - os: 'macos-14'
            xcode-version: '16.4'
          - os: 'macos-14'
            xcode-version: '16.3'
          - os: 'macos-14'
            xcode-version: '16.0'
          - os: 'macos-15'
            xcode-version: '15.4'
          - os: 'macos-15'
            xcode-version: '15.3'
          - os: 'macos-15'
            xcode-version: '15.2'
          - os: 'macos-15'
            xcode-version: '15.1'
          - os: 'macos-15'
            xcode-version: '15.0.1'

    steps:
      - name: Checkout the repo
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          path: ${{ env.WORKING_DIR }}

      - name: Update Go version using setup-go
        uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
        if: matrix.go != 'tip'
        with:
          # Include cache directives to allow proper caching. Without them, we
          # get setup-go "Restore cache failed" warnings.
          go-version: ${{ matrix.go }}
          cache: true
          cache-dependency-path: '**/go.sum'

      - name: Install Go bootstrap compiler
        uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
        if: matrix.go == 'tip'
        with:
          # Bootstrapping go tip requires 1.24
          # Include cache directives to allow proper caching. Without them, we
          # get setup-go "Restore cache failed" warnings.
          go-version: 1.24
          cache: true
          cache-dependency-path: '**/go.sum'

      - name: Update Go version manually
        if: matrix.go == 'tip'
        working-directory: ${{ github.workspace }}
        run: |
          git clone https://go.googlesource.com/go $HOME/gotip
          cd $HOME/gotip/src
          ./make.bash
          echo "GOROOT=$HOME/gotip" >> $GITHUB_ENV
          echo "RUN_STATICCHECK=false" >> $GITHUB_ENV
          echo "RUN_GOLANGCI_LINTER=false" >> $GITHUB_ENV
          echo "$HOME/gotip/bin:$PATH" >> $GITHUB_PATH

      - name: Set up Xcode
        uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1.6.0
        with:
          xcode-version: ${{ matrix.xcode-version }}

      - name: Fetch dependencies
        run: |
          brew install graphviz
          # Do not let tools interfere with the main module's go.mod.
          cd && go mod init tools
          go install honnef.co/go/tools/cmd/staticcheck@2025.1.1
          go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.8.0
          # Add PATH for installed tools.
          echo "$GOPATH/bin:$PATH" >> $GITHUB_PATH

      - name: Run the script
        run: |
          go version
          ./test.sh

      - name: Code coverage
        uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2
        with:
          files: ${{ env.WORKING_DIR }}/coverage.txt
          fail_ci_if_error: true
          token: ${{ secrets.CODECOV_TOKEN }}

  test-linux:
    runs-on: ${{ matrix.os }}
    defaults:
      run:
        working-directory: ${{ env.WORKING_DIR }}
    strategy:
      fail-fast: false
      matrix:
        go: ['1.24', '1.25', 'tip']
        os: ['ubuntu-24.04', 'ubuntu-22.04']
    steps:
      - name: Checkout the repo
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          path: ${{ env.WORKING_DIR }}

      - name: Update Go version using setup-go
        uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
        if: matrix.go != 'tip'
        with:
          # Include cache directives to allow proper caching. Without them, we
          # get setup-go "Restore cache failed" warnings.
          go-version: ${{ matrix.go }}
          cache: true
          cache-dependency-path: '**/go.sum'

      - name: Install Go bootstrap compiler
        uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
        if: matrix.go == 'tip'
        with:
          # Bootstrapping go tip requires 1.24
          # Include cache directives to allow proper caching. Without them, we
          # get setup-go "Restore cache failed" warnings.
          go-version: 1.24
          cache: true
          cache-dependency-path: '**/go.sum'

      - name: Update Go version manually
        if: matrix.go == 'tip'
        working-directory: ${{ github.workspace }}
        run: |
          git clone https://go.googlesource.com/go $HOME/gotip
          cd $HOME/gotip/src
          ./make.bash
          echo "GOROOT=$HOME/gotip" >> $GITHUB_ENV
          echo "RUN_STATICCHECK=false" >> $GITHUB_ENV
          echo "RUN_GOLANGCI_LINTER=false" >> $GITHUB_ENV
          echo "$HOME/gotip/bin" >> $GITHUB_PATH

      - name: Check chrome for browser tests
        run: |
          google-chrome --version
          which google-chrome

      - name: Add LLVM 14.0 repository to ensure llvm-symbolizer 14.0.0+ on Ubuntu 20.04
        if: matrix.os == 'ubuntu-20.04'
        run: |
          wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
          sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-14 main"
          sudo apt-get update

      - name: Install llvm-symbolizer
        run: |
          if [ "${{ matrix.os }}" = "ubuntu-20.04" ]; then
            sudo apt-get install -y llvm-14 clang-14
            sudo update-alternatives --install /usr/bin/llvm-symbolizer llvm-symbolizer /usr/bin/llvm-symbolizer-14 100
          else
            sudo apt-get update
            sudo apt-get install -y llvm clang
          fi

      - name: Fetch dependencies
        run: |
          sudo apt-get install graphviz
          # Do not let tools interfere with the main module's go.mod.
          cd && go mod init tools
          go install honnef.co/go/tools/cmd/staticcheck@2025.1.1
          go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.8.0
          # Add PATH for installed tools.
          echo "$GOPATH/bin:$PATH" >> $GITHUB_PATH

      - name: Check llvm-symbolizer installation
        run: |
          llvm-symbolizer --version

      - name: Run the script
        run: |
          go version
          ./test.sh

      - name: Code coverage
        uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2
        with:
          files: ${{ env.WORKING_DIR }}/coverage.txt
          fail_ci_if_error: true
          token: ${{ secrets.CODECOV_TOKEN }}

  test-windows:
    runs-on: windows-2022
    strategy:
      fail-fast: false
      matrix:
        go: ['1.24', '1.25']
    steps:
      - name: Checkout the repo
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          path: ${{ env.WORKING_DIR }}

      - name: Update Go version using setup-go
        uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
        with:
          # Include cache directives to allow proper caching. Without them, we
          # get setup-go "Restore cache failed" warnings.
          go-version: ${{ matrix.go }}
          cache: true
          cache-dependency-path: '**/go.sum'

      - name: Fetch Windows dependency
        uses: crazy-max/ghaction-chocolatey@dff3862348493b11fba2fbc49147b6d2dfe09b66 # v4.0.0
        with:
          args: install graphviz llvm

      - name: Run the test
        run: |
          go version
          # This is a workaround to make graphviz installed through choco work.
          # It generates a config file to tell dot what layout engine and
          # format types are available. See
          # https://github.com/google/pprof/issues/585 for more details.
          dot -c
          go env
          go build github.com/google/pprof
          go test -v ./...
        working-directory: ${{ env.WORKING_DIR }}

  check:
    if: always()
    runs-on: ubuntu-latest
    needs:
    - test-mac
    - test-linux
    - test-windows
    steps:
    - name: Decide whether the needed jobs succeeded or failed
      uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # v1.2.2
      with:
        jobs: ${{ toJSON(needs) }}


================================================
FILE: .gitignore
================================================
.DS_Store
*~
*.orig
*.exe
.*.swp
core
coverage.txt
pprof


================================================
FILE: AUTHORS
================================================
# This is the official list of pprof authors for copyright purposes.
# This file is distinct from the CONTRIBUTORS files.
# See the latter for an explanation.
# Names should be added to this file as:
# Name or Organization <email address>
# The email address is not required for organizations.
Google Inc.

================================================
FILE: CONTRIBUTING.md
================================================
Want to contribute? Great: read the page (including the small print at the end).

# Before you contribute

As an individual, sign the [Google Individual Contributor License
Agreement](https://cla.developers.google.com/about/google-individual) (CLA)
online. This is required for any of your code to be accepted.

Before you start working on a larger contribution, get in touch with us first
through the issue tracker with your idea so that we can help out and possibly
guide you. Coordinating up front makes it much easier to avoid frustration later
on.

# What to expect

All submissions (including by project members) are done via GitHub pull requests
and require a code review by a project member.

We expect contributions to be good, clean code following style and practices for
the language the contribution is in. The pprof source code is in Go with a bit
of JavaScript, CSS and HTML. If you are new to Go, read [Effective
Go](https://golang.org/doc/effective_go.html) and the [summary on typical
comments during Go code
reviews](https://github.com/golang/go/wiki/CodeReviewComments).

All contributions should include automated tests for the change. We are
continuously improving pprof automated testing and we can't accept changes that
are not helping that direction. Code coverage numbers are automatically
published in each pull request - we expect that number to go up.  Note that
adding a good test often requires more time than the fix itself - this is
expected and you should be prepared for that time investment.

Contributions that do not meet the above guidelines will get less attention and
will be slow to get accepted or won't be accepted at all. We will also likely
refuse to accept changes that have fairly limited audience but will require us
to commit to maintain them for foreseeable future. This includes support for
specific platforms, making internal pprof APIs public, etc.

# Development

The commands below assume `/tmp/pprof` as the location for the source code.
You can change it to a directory of your choice.

To get the source code, run

```
cd /tmp
git clone git@github.com:google/pprof.git
cd pprof
```

To run the tests, do

```
cd /tmp/pprof
go test -v ./...
(cd browsertests && go test)
```

When you wish to work with your own fork of the source (which is required to be
able to create a pull request), you'll want to get your fork repo as another Git
remote in the same `github.com/google/pprof` directory. Otherwise, if you'll `go
get` your fork directly, you'll be getting errors like `use of internal package
not allowed` when running tests.  To set up the remote do something like

```
cd /tmp/pprof
git remote add aalexand git@github.com:aalexand/pprof.git
git fetch aalexand
git checkout -b my-new-feature
# hack hack hack
go test -v ./...
(cd browsertests && go test)
git commit -a -m "Add new feature."
git push aalexand
```

where `aalexand` is your GitHub user ID. Then proceed to the GitHub UI to send a
code review.

# The small print

Contributions made by corporations are covered by a different agreement than the
one above, the [Software Grant and Corporate Contributor License
Agreement](https://cla.developers.google.com/about/google-corporate).


================================================
FILE: CONTRIBUTORS
================================================
# People who have agreed to one of the CLAs and can contribute patches.
# The AUTHORS file lists the copyright holders; this file
# lists people.  For example, Google employees are listed here
# but not in AUTHORS, because Google holds the copyright.
#
# https://developers.google.com/open-source/cla/individual
# https://developers.google.com/open-source/cla/corporate
#
# Names should be added to this file as:
#     Name <email address>
Raul Silvera <rsilvera@google.com>
Tipp Moseley <tipp@google.com>
Hyoun Kyu Cho <netforce@google.com>
Martin Spier <spiermar@gmail.com>
Taco de Wolff <tacodewolff@gmail.com>
Andrew Hunter <andrewhhunter@gmail.com>


================================================
FILE: LICENSE
================================================

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


================================================
FILE: README.md
================================================
[![Github Action CI](https://github.com/google/pprof/workflows/ci/badge.svg)](https://github.com/google/pprof/actions)
[![Codecov](https://codecov.io/gh/google/pprof/graph/badge.svg)](https://codecov.io/gh/google/pprof)
[![Go Reference](https://pkg.go.dev/badge/github.com/google/pprof/profile.svg)](https://pkg.go.dev/github.com/google/pprof/profile)

# Introduction

pprof is a tool for visualization and analysis of profiling data.

pprof reads a collection of profiling samples in profile.proto format and
generates reports to visualize and help analyze the data. It can generate both
text and graphical reports (through the use of the dot visualization package).

profile.proto is a protocol buffer that describes a set of callstacks
and symbolization information. A common usage is to represent a set of
sampled callstacks from statistical profiling. The format is
described on the [proto/profile.proto](./proto/profile.proto) file. For details on protocol
buffers, see https://developers.google.com/protocol-buffers

Profiles can be read from a local file, or over http. Multiple
profiles of the same type can be aggregated or compared.

If the profile samples contain machine addresses, pprof can symbolize
them through the use of the native binutils tools (addr2line and nm).

**This is not an official Google product.**

# Building pprof

Prerequisites:

- Go development kit of a [supported version](https://golang.org/doc/devel/release.html#policy).
  Follow [these instructions](http://golang.org/doc/code.html) to prepare
  the environment.

- Graphviz: http://www.graphviz.org/
  Optional, used to generate graphic visualizations of profiles

To build and install it:

    go install github.com/google/pprof@latest

The binary will be installed `$GOPATH/bin` (`$HOME/go/bin` by default).

# Basic usage

pprof can read a profile from a file or directly from a server via http.
Specify the profile input(s) in the command line, and use options to
indicate how to format the report.

## Generate a text report of the profile, sorted by hotness:

```
% pprof -top [main_binary] profile.pb.gz
Where
    main_binary:  Local path to the main program binary, to enable symbolization
    profile.pb.gz: Local path to the profile in a compressed protobuf, or
                   URL to the http service that serves a profile.
```

## Generate a graph in an SVG file, and open it with a web browser:

```
pprof -web [main_binary] profile.pb.gz
```

## Run pprof on interactive mode:

If no output formatting option is specified, pprof runs on interactive mode,
where reads the profile and accepts interactive commands for visualization and
refinement of the profile.

```
pprof [main_binary] profile.pb.gz

This will open a simple shell that takes pprof commands to generate reports.
Type 'help' for available commands/options.
```

## Run pprof via a web interface

If the `-http` flag is specified, pprof starts a web server at
the specified host:port that provides an interactive web-based interface to pprof.
Host is optional, and is "localhost" by default. Port is optional, and is a
random available port by default. `-http=":"` starts a server locally at
a random port.

```
pprof -http=[host]:[port] [main_binary] profile.pb.gz
```

The preceding command should automatically open your web browser at
the right page; if not, you can manually visit the specified port in
your web browser.

## Using pprof with Linux Perf

pprof can read `perf.data` files generated by the
[Linux perf](https://perf.wiki.kernel.org/index.php/Main_Page) tool by using the
`perf_to_profile` program from the
[perf_data_converter](https://github.com/google/perf_data_converter) package.

## Viewing disassembly on Windows

To view disassembly of profiles collected from Go programs compiled as Windows executables,
the executable must be built with `go build -buildmode=exe`. LLVM or GCC must be installed,
so required tools like `addr2line` and `nm` are available to `pprof`.

## Further documentation

See [doc/README.md](doc/README.md) for more detailed end-user documentation.

See [CONTRIBUTING.md](CONTRIBUTING.md) for contribution documentation.

See [proto/README.md](proto/README.md) for a description of the profile.proto format.


================================================
FILE: browsertests/README.md
================================================
Browser tests are separated out into a module of their own to avoid
polluting pprof dependencies with chromedp.

These tests can be run by executing the following in the top-level
of the pprof directory:

```shell
(cd browsertests && go test ./...)
```


================================================
FILE: browsertests/browser_test.go
================================================
// Copyright 2023 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package browsertests

import (
	"context"
	"fmt"
	"os/exec"
	"regexp"
	"runtime"
	"strings"
	"testing"
	"time"

	_ "embed"

	"github.com/chromedp/chromedp"
)

func maybeSkipBrowserTest(t *testing.T) {
	// Limit to just Linux for now since this is expensive and the
	// browser interactions should be platform agnostic.  If we ever
	// see a benefit from wider testing, we can relax this.
	if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
		t.Skip("This test only works on x86-64 Linux")
	}

	// Check that browser is available.
	if _, err := exec.LookPath("google-chrome"); err == nil {
		return
	}
	if _, err := exec.LookPath("chrome"); err == nil {
		return
	}
	t.Skip("chrome not available")
}

func TestTopTable(t *testing.T) {
	maybeSkipBrowserTest(t)

	prof := makeFakeProfile()
	server := makeTestServer(t, prof)
	ctx := newContext(context.Background(), t)

	err := chromedp.Run(ctx,
		chromedp.Navigate(server.URL+"/top"),
		chromedp.WaitVisible(`#toptable`, chromedp.ByID),

		// Check that fake profile entries show up in the right order.
		matchRegexp(t, "#node0", `200ms.*F2`),
		matchInOrder(t, "#toptable", "F2", "F3", "F1"),

		// Check sorting by cumulative count.
		chromedp.Click(`#cumhdr1`, chromedp.ByID),
		matchInOrder(t, "#toptable", "F1", "F2", "F3"),
	)
	if err != nil {
		t.Fatal(err)
	}
}

func TestFlameGraph(t *testing.T) {
	maybeSkipBrowserTest(t)

	prof := makeFakeProfile()
	server := makeTestServer(t, prof)
	ctx := newContext(context.Background(), t)

	var ignored []byte // Some chromedp.Evaluate() versions wants non-nil result argument
	err := chromedp.Run(ctx,
		chromedp.Navigate(server.URL),
		chromedp.Evaluate(jsTestFixture, &ignored),
		eval(t, jsCheckFlame),
	)
	if err != nil {
		t.Fatal(err)
	}
}

//go:embed testdata/testflame.js
var jsCheckFlame string

func TestSource(t *testing.T) {
	maybeSkipBrowserTest(t)

	prof := makeFakeProfile()
	server := makeTestServer(t, prof)
	ctx := newContext(context.Background(), t)

	err := chromedp.Run(ctx,
		chromedp.Navigate(server.URL+"/source?f=F3"),
		chromedp.WaitVisible(`#content`, chromedp.ByID),
		matchRegexp(t, "#content", `F3`),            // Header
		matchRegexp(t, "#content", `Total:.*100ms`), // Total for function
		matchRegexp(t, "#content", `\b22\b.*100ms`), // Line 22
	)
	if err != nil {
		t.Fatal(err)
	}
}

func newContext(ctx context.Context, t *testing.T) context.Context {
	opts := append(chromedp.DefaultExecAllocatorOptions[:],
		// Ubuntu 23+ enables AppArmor in a way that conflicts with Chrome's usage
		// of unprivileged user namespaces as part of the sandboxing. Since our
		// test does not visit any external websites, we don't really need the
		// sandbox, so disable it.
		chromedp.NoSandbox,
	)

	// browserDeadline is the deadline to use for browser tests. This is long to
	// reduce flakiness in CI workflows.
	const browserDeadline = time.Second * 90

	ctx, cancel := chromedp.NewExecAllocator(ctx, opts...)
	t.Cleanup(cancel)
	ctx, cancel = context.WithTimeout(ctx, browserDeadline)
	t.Cleanup(cancel)
	ctx, cancel = chromedp.NewContext(ctx)
	t.Cleanup(cancel)
	return ctx
}

// matchRegexp is a chromedp.Action that fetches the text of the first
// node that matched query and checks that the text matches regexp re.
func matchRegexp(t *testing.T, query, re string) chromedp.ActionFunc {
	return func(ctx context.Context) error {
		var value string
		err := chromedp.Text(query, &value, chromedp.ByQuery).Do(ctx)
		if err != nil {
			return fmt.Errorf("text %s: %v", query, err)
		}
		t.Logf("text %s:\n%s", query, value)
		m, err := regexp.MatchString(re, value)
		if err != nil {
			return err
		}
		if !m {
			return fmt.Errorf("%s: did not find %q in\n%s", query, re, value)
		}
		return nil
	}
}

// matchInOrder is a chromedp.Action that fetches the text of the first
// node that matched query and checks that the supplied sequence of
// strings occur in order in the text.
func matchInOrder(t *testing.T, query string, sequence ...string) chromedp.ActionFunc {
	return func(ctx context.Context) error {
		var value string
		err := chromedp.Text(query, &value, chromedp.ByQuery).Do(ctx)
		if err != nil {
			return fmt.Errorf("text %s: %v", query, err)
		}
		t.Logf("text %s:\n%s", query, value)
		remaining := value
		for _, s := range sequence {
			pos := strings.Index(remaining, s)
			if pos < 0 {
				return fmt.Errorf("%s: did not find %q in expected order %v  in\n%s", query, s, sequence, value)
			}
			remaining = remaining[pos+len(s):]
		}
		return nil
	}
}

// eval runs the specified javascript in the browser. The javascript must
// return an [][]any, where each of the []any starts with either "LOG" or
// "ERROR" (see testdata/testfixture.js).
func eval(t *testing.T, js string) chromedp.ActionFunc {
	return func(ctx context.Context) error {
		var result [][]any
		err := chromedp.Evaluate(js, &result).Do(ctx)
		if err != nil {
			return err
		}
		for _, s := range result {
			if len(s) > 0 && s[0] == "LOG" {
				t.Log(s[1:]...)
			} else if len(s) > 0 && s[0] == "ERROR" {
				t.Error(s[1:]...)
			} else {
				t.Error(s...) // Treat missing prefix as an error.
			}
		}
		return nil
	}
}

//go:embed testdata/testfixture.js
var jsTestFixture string


================================================
FILE: browsertests/go.mod
================================================
module github.com/google/pprof/browsertests

go 1.24.0

toolchain go1.24.9

// Use the version of pprof in this directory tree.
replace github.com/google/pprof => ../

require (
	github.com/chromedp/chromedp v0.13.6
	github.com/google/pprof v0.0.0
)

require (
	github.com/chromedp/cdproto v0.0.0-20250403032234-65de8f5d025b // indirect
	github.com/chromedp/sysutil v1.1.0 // indirect
	github.com/go-json-experiment/json v0.0.0-20250211171154-1ae217ad3535 // indirect
	github.com/gobwas/httphead v0.1.0 // indirect
	github.com/gobwas/pool v0.2.1 // indirect
	github.com/gobwas/ws v1.4.0 // indirect
	github.com/ianlancetaylor/demangle v0.0.0-20250417193237-f615e6bd150b // indirect
	golang.org/x/sys v0.32.0 // indirect
)


================================================
FILE: browsertests/go.sum
================================================
github.com/chromedp/cdproto v0.0.0-20250403032234-65de8f5d025b h1:jJmiCljLNTaq/O1ju9Bzz2MPpFlmiTn0F7LwCoeDZVw=
github.com/chromedp/cdproto v0.0.0-20250403032234-65de8f5d025b/go.mod h1:NItd7aLkcfOA/dcMXvl8p1u+lQqioRMq/SqDp71Pb/k=
github.com/chromedp/chromedp v0.13.6 h1:xlNunMyzS5bu3r/QKrb3fzX6ow3WBQ6oao+J65PGZxk=
github.com/chromedp/chromedp v0.13.6/go.mod h1:h8GPP6ZtLMLsU8zFbTcb7ZDGCvCy8j/vRoFmRltQx9A=
github.com/chromedp/sysutil v1.1.0 h1:PUFNv5EcprjqXZD9nJb9b/c9ibAbxiYo4exNWZyipwM=
github.com/chromedp/sysutil v1.1.0/go.mod h1:WiThHUdltqCNKGc4gaU50XgYjwjYIhKWoHGPTUfWTJ8=
github.com/go-json-experiment/json v0.0.0-20250211171154-1ae217ad3535 h1:yE7argOs92u+sSCRgqqe6eF+cDaVhSPlioy1UkA0p/w=
github.com/go-json-experiment/json v0.0.0-20250211171154-1ae217ad3535/go.mod h1:BWmvoE1Xia34f3l/ibJweyhrT+aROb/FQ6d+37F0e2s=
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.4.0 h1:CTaoG1tojrh4ucGPcoJFiAQUAsEWekEWvLy7GsVNqGs=
github.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakrc=
github.com/ianlancetaylor/demangle v0.0.0-20250417193237-f615e6bd150b h1:ogbOPx86mIhFy764gGkqnkFC8m5PJA7sPzlk9ppLVQA=
github.com/ianlancetaylor/demangle v0.0.0-20250417193237-f615e6bd150b/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo=
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw=
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=


================================================
FILE: browsertests/testdata/testfixture.js
================================================
// TestFixture records log messages and errors in an array that will
// be returned to Go code. Each element in the result array is either
// an array of the form ["LOG", ...], or ["ERROR", ...].
class TestFixture {
  constructor() {
    this.context = "";  // Added to front of all log and error messages.
    this.result = [];
  }

  run(name, subtest) {
    this.result.push(["LOG", "===", name]);
    this.context = "";
    subtest();
    this.context = "";
  }

  // setContext arranges to add name(args) to all messages added
  // until the next setContext or run call.
  setContext(name, ...args) {
    this.context = name + "(" + args.join(",") + ")";
  }

  log(...args) {
    this.result.push(["LOG", this.context, ...args]);
  }

  err(...args) {
    this.result.push(["ERROR", this.context, ...args]);
  }
}


================================================
FILE: browsertests/testdata/testflame.js
================================================
function TestFlame() {
  const PADDING = 2; // Matches PADDING in stackViewer

  const test = new TestFixture();
  const chart = document.getElementById("stack-chart");
  if (!chart) {
    test.err("could not find stack-chart");
    return;
  }
  const chartRect = chart.getBoundingClientRect();

  // Create map from box text to DOM element.
  // TODO: Generalize to support multiple boxes for a given piece of text.
  let boxMap;
  fetchBoxes();
  function fetchBoxes() {
    boxMap = new Map();
    const boxes = document.querySelectorAll(".boxbg");
    for (let box of boxes) {
      const text = box.innerText;
      boxMap.set(text, box);
    }
  }

  // rect gets the bounding box for box with text t.
  function rect(t) {
    const elem = boxMap.get(t);
    if (!elem) {
      test.err("did not find", t);
      return null;
    }
    return elem.getBoundingClientRect();
  }

  // checkCalls checks that box with text a is positioned w.r.t. box with
  // text b to indicate a call from a to b.  Expect a gap of the specified
  // number of rows.
  function checkCalls(a, b, gap = 0) {
    test.setContext("checkCalls", a, b, gap);
    const ra = rect(a);
    const rb = rect(b);
    if (!ra || !rb) return;

    const pixelGap = gap * rb.height;
    if (rb.top != ra.bottom + pixelGap) {
      test.err("not above");
    }
    // TODO: Allow checking boxes above pivots.
    if (rb.left < ra.left || rb.right > ra.right) {
      test.err("horizontal span of", a, "is not nested inside horizontal span of", b);
    }
  }

  // checkWidth checks that the width of the box with text t is approximately
  // the specified fraction of the total width.
  function checkWidth(t, fraction) {
    test.setContext("checkWidth", t, fraction);
    const r = rect(t);
    if (!r) return;
    const expect = (chartRect.width - 2*PADDING) * fraction;
    if (r.width < expect*0.95 || r.width > expect*1.05) {
      test.err("bad width", r.width, "expecting ~", expect);
    }
  }

  // Fake profile has the following stacks:
  //    100 F1 F2 F3
  //    200 F1 F2
  test.run("initial", function() {
    checkCalls("root", "F1");
    checkCalls("F1", "F2");
    checkCalls("F2", "F3");
    checkWidth("root", 300/300);
    checkWidth("F1", 300/300);
    checkWidth("F2", 300/300);
    checkWidth("F3", 100/300);
  });

  test.run("Pivot F3", function() {
    boxMap.get("F3").click();
    fetchBoxes();
    checkCalls("root", "F1");
    checkCalls("F1", "F2");
    checkCalls("F2", "F3", 1);
    checkWidth("root", 100/100);
    checkWidth("F1", 100/100);
    checkWidth("F2", 100/100);
    checkWidth("F3", 100/100);
  });

  test.run("NavigateWithoutPivot", function() {
    // Clear pivot
    boxMap.get("root").click();

    // Trigger link update.
    const btn = document.getElementById("graphbtn");
    if (!btn) {
      test.err("no graph button on page");
      return;
    }
    const event = new Event("mouseenter");
    btn.dispatchEvent(event);

    // Check that URL does not contain a focus parameter.
    test.log(btn.href);
    const url = new URL(btn.href);
    if (url.searchParams.has('f')) {
      test.err("unexpected focus parameter in URL", btn.href);
    }
  });

  test.run("Units", function() {
    function checkUnitText(unit, v, expect) {
      const result = pprofUnitText(v, unit);
      if (result != expect) {
        test.err("bad text for", v, unit, ":", result, "expecting:", expect);
      }
    }

    // Time units, plus logic tests.
    checkUnitText("s", 0.51e-9, "0.51ns");
    checkUnitText("s", 3e-9, "3ns");
    checkUnitText("s", 1.23e-6, "1.23us");
    checkUnitText("s", 0.04, "40ms");
    checkUnitText("s", 1, "1s");
    checkUnitText("s", 3599, "3599s");
    checkUnitText("s", 3600, "1hrs");

    // Sanity check for byte units.
    checkUnitText("B", 2*1048576, "2MB");

    // Unknown unit.
    checkUnitText("cm", 100, "100cm");
  });

  return test.result;
}
TestFlame();


================================================
FILE: browsertests/testutils.go
================================================
package browsertests

import (
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"regexp"
	"runtime"
	"testing"
	"time"

	"github.com/google/pprof/driver"
	"github.com/google/pprof/profile"
)

func makeTestServer(t testing.TB, prof *profile.Profile) *httptest.Server {
	if runtime.GOOS == "nacl" || runtime.GOOS == "js" {
		t.Skip("test assumes tcp available")
	}

	// Custom http server creator
	var server *httptest.Server
	serverCreated := make(chan bool)
	creator := func(a *driver.HTTPServerArgs) error {
		server = httptest.NewServer(http.HandlerFunc(
			func(w http.ResponseWriter, r *http.Request) {
				if h := a.Handlers[r.URL.Path]; h != nil {
					h.ServeHTTP(w, r)
				}
			}))
		serverCreated <- true
		return nil
	}

	// Start server and wait for it to be initialized
	go func() {
		err := driver.PProf(&driver.Options{
			Obj:        fakeObjTool{},
			UI:         testUI{t},
			Fetch:      testFetcher{prof},
			HTTPServer: creator,
			Flagset: testFlags{
				"http":       "unused:1234",
				"no_browser": true,
			},
		})
		if err != nil {
			panic(err)
		}
	}()
	<-serverCreated

	// Close the server when the test is done.
	t.Cleanup(server.Close)

	return server
}

// Fake test implementations of types needed by pprof driver.

const addrBase = 0x1000
const fakeSource = "testdata/file1000.src"

type fakeObj struct{}

func (f fakeObj) Close() error                        { return nil }
func (f fakeObj) Name() string                        { return "testbin" }
func (f fakeObj) ObjAddr(addr uint64) (uint64, error) { return addr, nil }
func (f fakeObj) BuildID() string                     { return "" }
func (f fakeObj) SourceLine(addr uint64) ([]driver.Frame, error) {
	return nil, fmt.Errorf("SourceLine unimplemented")
}
func (f fakeObj) Symbols(r *regexp.Regexp, addr uint64) ([]*driver.Sym, error) {
	return []*driver.Sym{
		{
			Name: []string{"F1"}, File: fakeSource,
			Start: addrBase, End: addrBase + 10,
		},
		{
			Name: []string{"F2"}, File: fakeSource,
			Start: addrBase + 10, End: addrBase + 20,
		},
		{
			Name: []string{"F3"}, File: fakeSource,
			Start: addrBase + 20, End: addrBase + 30,
		},
	}, nil
}

type fakeObjTool struct{}

func (obj fakeObjTool) Open(file string, start, limit, offset uint64, relocationSymbol string) (driver.ObjFile, error) {
	return fakeObj{}, nil
}

func (obj fakeObjTool) Disasm(file string, start, end uint64, intelSyntax bool) ([]driver.Inst, error) {
	return []driver.Inst{
		{Addr: addrBase + 10, Text: "f1:asm", Function: "F1", Line: 3},
		{Addr: addrBase + 20, Text: "f2:asm", Function: "F2", Line: 11},
		{Addr: addrBase + 30, Text: "d3:asm", Function: "F3", Line: 22},
	}, nil
}

func makeFakeProfile() *profile.Profile {
	// Three functions: F1, F2, F3 with three lines, 11, 22, 33.
	funcs := []*profile.Function{
		{ID: 1, Name: "F1", Filename: fakeSource, StartLine: 3},
		{ID: 2, Name: "F2", Filename: fakeSource, StartLine: 5},
		{ID: 3, Name: "F3", Filename: fakeSource, StartLine: 7},
	}
	lines := []profile.Line{
		{Function: funcs[0], Line: 11},
		{Function: funcs[1], Line: 22},
		{Function: funcs[2], Line: 33},
	}
	mapping := []*profile.Mapping{
		{
			ID:             1,
			Start:          addrBase,
			Limit:          addrBase + 100,
			Offset:         0,
			File:           "testbin",
			HasFunctions:   true,
			HasFilenames:   true,
			HasLineNumbers: true,
		},
	}

	// Three interesting addresses: base+{10,20,30}
	locs := []*profile.Location{
		{ID: 1, Address: addrBase + 10, Line: lines[0:1], Mapping: mapping[0]},
		{ID: 2, Address: addrBase + 20, Line: lines[1:2], Mapping: mapping[0]},
		{ID: 3, Address: addrBase + 30, Line: lines[2:3], Mapping: mapping[0]},
	}

	// Two stack traces.
	return &profile.Profile{
		PeriodType:    &profile.ValueType{Type: "cpu", Unit: "milliseconds"},
		Period:        1,
		DurationNanos: 10e9,
		SampleType: []*profile.ValueType{
			{Type: "cpu", Unit: "milliseconds"},
		},
		Sample: []*profile.Sample{
			{
				Location: []*profile.Location{locs[2], locs[1], locs[0]},
				Value:    []int64{100},
			},
			{
				Location: []*profile.Location{locs[1], locs[0]},
				Value:    []int64{200},
			},
		},
		Location: locs,
		Function: funcs,
		Mapping:  mapping,
	}
}

type testFlags map[string]any

func (flags testFlags) Bool(name string, def bool, usage string) *bool {
	return getFlag(flags, name, def)
}
func (flags testFlags) Int(name string, def int, usage string) *int {
	return getFlag(flags, name, def)
}
func (flags testFlags) Float64(name string, def float64, usage string) *float64 {
	return getFlag(flags, name, def)
}
func (flags testFlags) String(name string, def string, usage string) *string {
	return getFlag(flags, name, def)
}
func (flags testFlags) StringList(name string, def string, usage string) *[]*string {
	return getFlag(flags, name, []*string{}) // Not supported, so return an empty list.
}
func (flags testFlags) ExtraUsage() string          { return "" }
func (flags testFlags) AddExtraUsage(eu string)     {}
func (flags testFlags) Parse(usage func()) []string { return []string{"test", "bin"} }

var _ driver.FlagSet = testFlags{}

func getFlag[T any](flags testFlags, name string, def T) *T {
	result := &def
	if v, ok := flags[name]; ok {
		*result = v.(T)
	}
	return result
}

type testUI struct {
	T testing.TB
}

func (ui testUI) ReadLine(_ string) (string, error)     { return "", io.EOF }
func (ui testUI) IsTerminal() bool                      { return false }
func (ui testUI) WantBrowser() bool                     { return false }
func (ui testUI) SetAutoComplete(_ func(string) string) {}
func (ui testUI) Print(args ...interface{})             {} // discard
func (ui testUI) PrintErr(args ...interface{}) {
	ui.T.Error("unexpected error: " + fmt.Sprint(args...))
}

var _ driver.UI = testUI{}

type testFetcher struct {
	profile *profile.Profile
}

func (f testFetcher) Fetch(source string, duration, timeout time.Duration) (*profile.Profile, string, error) {
	// http://pproftest.local prevents file from being saved.
	return f.profile, "http://pproftest.local", nil
}

var _ driver.Fetcher = testFetcher{}


================================================
FILE: doc/README.md
================================================
# pprof

pprof is a tool for visualization and analysis of profiling data.

pprof reads a collection of profiling samples in profile.proto format and
generates reports to visualize and help analyze the data. It can generate both
text and graphical reports (through the use of the dot visualization package).

profile.proto is a protocol buffer that describes a set of callstacks
and symbolization information. A common usage is to represent a set of
sampled callstacks from statistical profiling. The format is
described on the proto/profile.proto file. For details on protocol
buffers, see https://developers.google.com/protocol-buffers

Profiles can be read from a local file, or over http. Multiple
profiles of the same type can be aggregated or compared.

If the profile samples contain machine addresses, pprof can symbolize
them through the use of the native binutils tools (addr2line and nm).

# pprof profiles

pprof operates on data in the profile.proto format. Each profile is a collection
of samples, where each sample is associated to a point in a location hierarchy,
one or more numeric values, and a set of labels. Often these profiles represents
data collected through statistical sampling of a program, so each sample
describes a program call stack and a number or value of samples collected at a
location. pprof is agnostic to the profile semantics, so other uses are
possible. The interpretation of the reports generated by pprof depends on the
semantics defined by the source of the profile.

# Usage modes

There are few different ways of using `pprof`.

## Report generation

If a report format is requested on the command line:

    pprof <format> [options] source

pprof will generate a report in the specified format and exit.
Formats can be either text, or graphical. See below for details about
supported formats, options, and sources.

## Interactive terminal use

Without a format specifier:

    pprof [options] source

pprof will start an interactive shell in which the user can type
commands.  Type `help` to get online help.

## Web interface

If a host:port is specified on the command line:

    pprof -http=[host]:[port] [options] source

pprof will start serving HTTP requests on the specified port.  Visit
the HTTP url corresponding to the port (typically `http://<host>:<port>/`)
in a browser to see the interface.

# Details

The objective of pprof is to generate a report for a profile. The report is
generated from a location hierarchy, which is reconstructed from the profile
samples. Each location contains two values:

* *flat*: the value of the location itself.
* *cum*: the value of the location plus all its descendants.

Samples that include a location multiple times (e.g. for recursive functions)
are counted only once per location.

## Options

*options* configure the contents of a report. Each option has a value,
which can be boolean, numeric, or strings. While only one format can
be specified, most options can be selected independently of each
other.

Some common pprof options are:

* **-flat** [default], **-cum**: Sort entries based on their flat or cumulative
  value respectively, on text reports.
* **-functions** [default], **-filefunctions**, **-files**, **-lines**,
  **-addresses**: Generate the report using the specified granularity.
* **-noinlines**: Attribute inlined functions to their first out-of-line caller.
  For example, a command like `pprof -list foo -noinlines profile.pb.gz` can be
  used to produce the annotated source listing attributing the metrics in the
  inlined functions to the out-of-line calling line.
* **-nodecount= _int_:** Maximum number of entries in the report. pprof will
  only print this many entries and will use heuristics to select which entries
  to trim.
* **-focus= _regex_:** Only include samples that include a report entry matching
  *regex*.
* **-ignore= _regex_:** Do not include samples that include a report entry
  matching *regex*.
* **-show\_from= _regex_:** Do not show entries above the first one that
  matches *regex*.
* **-show= _regex_:** Only show entries that match *regex*.
* **-hide= _regex_:** Do not show entries that match *regex*.

Each sample in a profile may include multiple values, representing different
entities associated to the sample. pprof reports include a single sample value,
which by convention is the last one specified in the report. The `sample_index=`
option selects which value to use, and can be set to a number (from 0 to the
number of values - 1) or the name of the sample value.

Sample values are numeric values associated to a unit. If pprof can recognize
these units, it will attempt to scale the values to a suitable unit for
visualization. The `unit=` option will force the use of a specific unit. For
example, `unit=sec` will force any time values to be reported in
seconds. pprof recognizes most common time and memory size units.

## Tags

Samples in a profile may have tags. These tags have a name and a value. The
value can be either numeric or a string; the numeric values can be associated
with a unit. Tags are used as additional dimensions that the sample values can
be broken by. The most common use of tags is selecting samples from a profile
based on the tag values. pprof also supports tags at the visualization time.

### Tag filtering

The `-tagfocus` option is the most used option for selecting data in a profile
based on tag values. It has the syntax of **-tagfocus=_regex_** or
**-tagfocus=_range_:** which will restrict the data to samples with tags matched
by regexp or in range. The `-tagignore` option has the identical syntax and can
be used to filter out the samples that have matching tags. If both `-tagignore`
and `-tagfocus` are specified and match a given sample, then the sample will be
discarded.

When using `-tagfocus=regex` and `-tagignore=regex`, the regex will be compared
to each value associated with each tag. If one specifies a value
like `regex1,regex2`, then only samples with a tag value matching `regex1`
and a tag value matching `regex2` will be kept.

In addition to being able to filter on tag values, one can specify the name of
the tag which a certain value must be associated with using the notation
`-tagfocus=tagName=value`. Here, the `tagName` must match the tag's name
exactly, and the value can be either a regex or a range. If one specifies
a value like `regex1,regex2`, then samples with a tag value (paired with the
specified tag name) matching either `regex1` or matching `regex2` will match.

Here are examples explaining how `-tagfocus` can be used:

* `-tagfocus 128kb:512kb` accepts a sample iff it has any numeric tag with
  memory value in the specified range.
* `-tagfocus mytag=128kb:512kb` accepts a sample iff it has a numeric tag
  `mytag` with memory value in the specified range. There isn't a way to say
   `-tagfocus mytag=128kb:512kb,16kb:32kb`
   or `-tagfocus mytag=128kb:512kb,mytag2=128kb:512kb`. Just single value or
   range for numeric tags.
* `-tagfocus someregex` accepts a sample iff it has any string tag with
  `tagName:tagValue` string matching specified regexp. In the future, this
  will change to accept sample iff it has any string tag with `tagValue` string
  matching specified regexp.
* `-tagfocus mytag=myvalue1,myvalue2` matches if either of the two tag values
  are present.

### Tag visualization

To list the tags and their values available in a profile use **-tags** option.
It will output the available tags and their values as well as the breakdown of
the sample value by the values of each tag.

The pprof callgraph reports, such as `-web` or raw `-dot`, will automatically
visualize the values for all tags as pseudo nodes in the graph. Use `-tagshow`
and `-taghide` options to limit what tags are displayed. The options accept a
regular expression that is matched against the tag name to show or hide it
respectively.

Options `-tagroot` and `-tagleaf` can be used to create pseudo stack frames to
the profile samples. For example, `-tagroot=mytag` will add stack frames at the
root of the profile call tree with the value of the tag for the corresponding
samples. Similarly, `-tagleaf=mytag` will add such stack frames as leaf nodes of
each sample. These options are useful when visualizing a profile in tree formats
such as the tree view in the `-http` mode web UI.

## Text reports

pprof text reports show the location hierarchy in text format.

* **-text:** Prints the location entries, one per line, including the flat and
  cum values.
* **-tree:** Prints each location entry with its predecessors and successors.
* **-peek= _regex_:** Print the location entry with all its predecessors and
  successors, without trimming any entries.
* **-traces:** Prints each sample with a location per line.

## Graphical reports

pprof can generate graphical reports on the DOT format, and convert them to
multiple formats using the graphviz package.

These reports represent the location hierarchy as a graph, with a report entry
represented as a node. Nodes are removed using heuristics to limit the size of
the graph, controlled by the *nodecount* option.

* **-dot:** Generates a report in .dot format. All other formats are generated
  from this one.
* **-svg:** Generates a report in SVG format.
* **-web:** Generates a report in SVG format on a temp file, and starts a web
  browser to view it.
* **-png, -jpg, -gif, -pdf:** Generates a report in these formats.

### Interpreting the Callgraph

* **Node Color**:
  * large positive cum values are red.
  * large negative cum values are green; negative values are most likely to
    appear during profile comparison, see [this section](#comparing-profiles)
    for details.
  * cum values close to zero are grey.

* **Node Font Size**:
  * larger font size means larger absolute flat values.
  * smaller font size means smaller absolute flat values.

* **Edge Weight**:
  * thicker edges indicate more resources were used along that path.
  * thinner edges indicate fewer resources were used along that path.

* **Edge Color**:
  * large positive values are red.
  * large negative values are green.
  * values close to zero are grey.

* **Dashed Edges**: some locations between the two connected locations were
  removed.

* **Solid Edges**: one location directly calls the other.

* **"(inline)" Edge Marker**: the call has been inlined into the caller.

Let's consider the following example graph:

![callgraph](images/callgraph.png)

* For nodes:
  * `(*Rand).Read` has a small flat value and a small cum value because the
    the font is small and the node is grey.
  * `(*compressor).deflate` has a large flat value and a large cum value because the font
    is large and the node is red.
  * `(*Writer).Flush` has a small flat value and a large cum value because the font is
    small and the node is red.

* For edges:
  * the edge between `(*Writer).Write` and `(*compressor).write`:
    * Since it is a dashed edge, some nodes were removed between those two.
    * Since it is thick and red, more resources were used in call stacks between
    those two nodes.
  * the edge between `(*Rand).Read` and `read`:
    * Since it is a dashed edge, some nodes were removed between those two.
    * Since it is thin and grey, fewer resources were used in call stacks
    between those two nodes.
  * the edge between `read` and `(*rngSource).Int63`:
    * Since it is a solid edge, there are no nodes between those two (i.e. it
      was a direct call).
    * Since it is thin and grey, fewer resources were used in call stacks
      between those two nodes.

## Annotated code

pprof can also generate reports of annotated source with samples associated to
them. For these, the source or binaries must be locally available, and the
profile must contain data with the appropriate level of detail.

pprof will look for source files on its current working directory and all its
ancestors. pprof will look for binaries on the directories specified in the
`$PPROF_BINARY_PATH` environment variable, by default `$HOME/pprof/binaries`
(`%USERPROFILE%\pprof\binaries` on Windows). It will look binaries up by name,
and if the profile includes linker build ids, it will also search for them in
a directory named as the build id.

pprof uses the binutils tools to examine and disassemble the binaries. By
default it will search for those tools in the current path, but it can also
search for them in a directory pointed to by the environment variable
`$PPROF_TOOLS`.

* **-list= _regex_:** Generates an annotated source listing for functions
  matching *regex*, with flat/cum values for each source line.
* **-disasm= _regex_:** Generates an annotated disassembly listing for
  functions matching *regex*.
* **-weblist= _regex_:** Generates a source/assembly combined annotated listing
  for functions matching *regex*, and starts a web browser to display it.

## Comparing profiles

pprof can subtract one profile from another, provided the profiles are of
compatible types (i.e. two heap profiles). pprof has two options which can be
used to specify the filename or URL for a profile to be subtracted from the
source profile:

* **-diff_base= _profile_:** useful for comparing two profiles. Percentages in
the output are relative to the total of samples in the diff base profile.

* **-base= _profile_:** useful for subtracting a cumulative profile, like a
[golang block profile](https://golang.org/doc/diagnostics.html#profiling),
from another cumulative profile collected from the same program at a later time.
When comparing cumulative profiles collected on the same program, percentages in
the output are relative to the difference between the total for the source
profile and the total for the base profile.

The **-normalize** flag can be used when a base profile is specified with either
the `-diff_base` or the `-base` option. This flag scales the source profile so
that the total of samples in the source profile is equal to the total of samples
in the base profile prior to subtracting the base profile from the source
profile. Useful for determining the relative differences between profiles, for
example, which profile has a larger percentage of CPU time used in a particular
function.

When using the **-diff_base** option, some report entries may have negative
values. If the merged profile is output as a protocol buffer, all samples in the
diff base profile will have a label with the key "pprof::base" and a value of
"true". If pprof is then used to look at the merged profile, it will behave as
if separate source and base profiles were passed in.

When using the **-base** option to subtract one cumulative profile from another
collected on the same program at a later time, percentages will be relative to
the difference between the total for the source profile and the total for
the base profile, and all values will be positive. In the general case, some
report entries may have negative values and percentages will be relative to the
total of the absolute value of all samples when aggregated at the address level.

# Fetching profiles

pprof can read profiles from a file or directly from a URL over http or https.
Its native format is a gzipped profile.proto file, but it can
also accept some legacy formats generated by
[gperftools](https://github.com/gperftools/gperftools).

When fetching from a URL handler, pprof accepts options to indicate how much to
wait for the profile.

* **-seconds= _int_:** Makes pprof request for a profile with the specified
  duration in seconds. Only makes sense for profiles based on elapsed time, such
  as CPU profiles.
* **-timeout= _int_:** Makes pprof wait for the specified timeout when
  retrieving a profile over http. If not specified, pprof will use heuristics to
  determine a reasonable timeout.

pprof also accepts options which allow a user to specify TLS certificates to
use when fetching or symbolizing a profile from a protected endpoint. For more
information about generating these certificates, see
https://docs.docker.com/engine/security/https/.

* **-tls\_cert= _/path/to/cert_:** File containing the TLS client certificate
  to be used when fetching and symbolizing profiles.
* **-tls\_key= _/path/to/key_:** File containing the TLS private key to be used
  when fetching and symbolizing profiles.
* **-tls\_ca= _/path/to/ca_:** File containing the certificate authority to be
  used when fetching and symbolizing profiles.

pprof also supports skipping verification of the server's certificate chain and
host name when collecting or symbolizing a profile. To skip this verification,
use "https+insecure" in place of "https" in the URL.

If multiple profiles are specified, pprof will fetch them all and merge
them. This is useful to combine profiles from multiple processes of a
distributed job. The profiles may be from different programs but must be
compatible (for example, CPU profiles cannot be combined with heap profiles).

## Symbolization

pprof can add symbol information to a profile that was collected only with
address information. This is useful for profiles for compiled languages, where
it may not be easy or even possible for the profile source to include function
names or source coordinates.

pprof can extract the symbol information locally by examining the binaries using
the binutils tools, or it can ask running jobs that provide a symbolization
interface.

pprof will attempt symbolizing profiles by default, and its `-symbolize` option
provides some control over symbolization:

* **-symbolize=none:** Disables any symbolization from pprof.

* **-symbolize=local:** Only attempts symbolizing the profile from local
  binaries using the binutils tools.

* **-symbolize=remote:** Only attempts to symbolize running jobs by contacting
  their symbolization handler.

For local symbolization, pprof will look for the binaries on the paths specified
by the profile, and then it will search for them on the path specified by the
environment variable `$PPROF_BINARY_PATH`. Also, the name of the main binary can
be passed directly to pprof as its first parameter, to override the name or
location of the main binary of the profile, like this:

    pprof /path/to/binary profile.pb.gz

By default pprof will attempt to demangle and simplify C++ names, to provide
readable names for C++ symbols. It will aggressively discard template and
function parameters. This can be controlled with the `-symbolize=demangle`
option. Note that for remote symbolization mangled names may not be provided by
the symbolization handler.

* **-symbolize=demangle=none:** Do not perform any demangling. Show mangled
  names if available.

* **-symbolize=demangle=full:** Demangle, but do not perform any
  simplification. Show full demangled names if available.

* **-symbolize=demangle=templates:** Demangle, and trim function parameters, but
  not template parameters.

# Web Interface

When the user requests a web interface (by supplying an `-http=[host]:[port]`
argument on the command-line), pprof starts a web server and opens a browser
window pointing at that server. The web interface provided by the server allows
the user to interactively view profile data in multiple formats.

## Views

The top of the display is a header that contains some buttons and menus.  The
`View` menu allows the user to switch between different visualizations of the
profile. The available views are described here:

### Graph

The default view in the local web interface displays a graph where the nodes are
functions, and edges indicate caller/callee relations.

Note: You can drag the display around with the mouse button held down, or zoom
in and out using a mouse scroll-wheel or pinch/expand touch gestures.

![Graph view](images/webui/graph.png)

E.g., `FormatPack` has an outgoing edge to `FormatUntyped` that indicates that
the former calls the latter. The number along the edge (5.72s) indicates the
amount of time that was spent in `FormatUntyped` (and its callees) when called
from `FormatPack`.

See [earlier explanation](#interpreting-the-callgraph) for more details.

### Flame graph

Switching to the `Flame graph` view (via the `View` menu) will display a [flame
graph](https://www.brendangregg.com/flamegraphs.html). This view provides a
compact representation of caller/callee relations:

![Flame graph](images/webui/flame.png)

Boxes on this view correspond to stack frames in the profile. Caller boxes are
directly above callee boxes. The width of each box is proportional to the sum of
the sample value of profile samples where that frame was present on the call
stack. Children of a particular box are laid out left to right in decreasing
size order.

E.g., here we see that `FormatPack` is right above `FormatUntyped`, which
indicates that the former calls the latter. The width of `FormatUntyped`
corresponds to the fraction of time accounted for by this call.

Names displayed in different boxes may have different font sizes. These size
differences are due to an attempt to fit as much of the name into the box as
possible; no other interpretation should be placed on the size.

Boxes are colored according to the name of the package in which the corresponding
function occurs. E.g., in C++ profiles all frames corresponding to `std::` functions
will be assigned the same color.

#### Viewing callers

Traditional flame graphs provide a top-down view: it is easy to see the
functions called by a particular function, but harder to find callers of a
particular function. E.g., in the linked example there are multiple occurrences
of `FormatUntyped` since it has multiple callers.

Pprof's flame graph extend the traditional model: when a function is selected,
the graph changes to show call-stacks leading that function. Therefore, clicking
on any of the `FormatUntyped` boxes will show the call stacks that end up
calling `FormatUntyped`:

![Flame graph showing multiple callers](images/webui/flame-multi.png)

#### Diff mode

When using the **--diff_base** option, box width is proportional to the sum of
the increases and decreases in the sub-tree rooted at box. E.g., if the cost of
one child of box decreases by 150 and the cost of another child increases by
200, the box width will be proportional to 150+200. The net increase or decrease
(the preceding example has a net increase of 200-150, i.e., 50) is indicated by
a shaded region. The size of the shaded region is proportional to the net
increase or net decrease. The shading is red for a net increase, and green for a
net decrease.

#### Inlining

Inlining is indicated by the absence of a horizontal border between a caller and
a callee. E.g., suppose X calls Y calls Z and the call from Y to Z is inlined into
Y. There will be a black border between X and Y, but no border between Y and Z.

### Annotated Source Code

Let's try to dig into what is going on inside `FormatUntyped` by viewing its
source-code annotated with performance data. First, right-click on the box for
the function to get a context menu.

![Flame menu](images/webui/flame-menu.png)

Select `Show source in new tab`. That will create a new tab that displays source
code for the function.

Note: You can also display source code by selecting `Source` from the `View`
menu, but only do so if you are focused on just one or a few routines since
source code display can be very slow and voluminous when multiple functions are
being viewed.

![Source listing](images/webui/source.png)

Each source line is annotated with the time spent in that source line. There are
two numbers (e.g., 840ms and 6.17s on line 207 in the screenshot). The first
number does not count time spent in functions called from the source line, the
second number includes that time.

Let's dig down a bit more by clicking on line 207. That will expand the display
to include the source code for inlined function calls, as well as the
corresponding assembly code.

![Expanded source listing](images/webui/source-expanded.png)

The assembly code is displayed in green. Source code for inlined functions is
displayed in blue and is indented by its inlining level. For example, the
indentation indicates that the `ConvertAll` call on line 207 is inlined, and it
in turn has an inlined call to `has_parsed_conversion`, which in turn expands to
a `cmpq` instruction.

### Disassembly

Sometimes it is helpful to view just the disassembly in instruction order
without interleaving with source code. You can achieve this by selecting
`Disassemble`" from the `View` menu.

Note: Do not select `Disassemble` unless you are focused on just one or a few
routines since disassembly can be very slow and voluminous when multiple
functions are being viewed.

![Disassembly](images/webui/disasm.png)

### Top Functions

You may sometimes find a table that displays just the top functions in the
profile helpful.

![Top functions](images/webui/top.png)

The table shows numbers (and percentages) for two different metrics:

*   `flat`: profile samples in this function
*   `cum`: (cumulative) profile samples in this function and its callees

The table is initially sorted in decreasing order of `flat`. Clicking on the
`Cum` table header will sort it in decreasing order of samples in the function
and its callees.

### Peek

This view shows callers / callees per function in a simple textual format.
The Flame graph view is typically more helpful.

## Config

The `Config` menu allows the user to save the current refinement
settings (e.g., the focus and hide list) as a named configuration. A
saved configuration can later be re-applied to reinstitue the saved
refinements. The `Config` menu contains:

**Save as ...**: shows a dialog where the user can type in a
configuration name. The current refinement settings are saved under
the specified name.

**Default**: switches back to the default view by removing all refinements.

The `Config` menu also contains an entry per named
configuration. Selecting such an entry applies that configuration. The
currently selected entry is marked with a ✓. Clicking on the 🗙 on the
right-hand side of such an entry deletes the configuration (after
prompting the user to confirm).

## TODO: cover the following issues:

*   Overall layout
*   Other menu entries


================================================
FILE: driver/driver.go
================================================
// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package driver provides an external entry point to the pprof driver.
package driver

import (
	"io"
	"maps"
	"net/http"
	"regexp"
	"time"

	internaldriver "github.com/google/pprof/internal/driver"
	"github.com/google/pprof/internal/plugin"
	"github.com/google/pprof/profile"
)

// PProf acquires a profile, and symbolizes it using a profile
// manager. Then it generates a report formatted according to the
// options selected through the flags package.
func PProf(o *Options) error {
	return internaldriver.PProf(o.internalOptions())
}

func (o *Options) internalOptions() *plugin.Options {
	var obj plugin.ObjTool
	if o.Obj != nil {
		obj = &internalObjTool{o.Obj}
	}
	var sym plugin.Symbolizer
	if o.Sym != nil {
		sym = &internalSymbolizer{o.Sym}
	}
	var httpServer func(args *plugin.HTTPServerArgs) error
	if o.HTTPServer != nil {
		httpServer = func(args *plugin.HTTPServerArgs) error {
			return o.HTTPServer(((*HTTPServerArgs)(args)))
		}
	}
	return &plugin.Options{
		Writer:        o.Writer,
		Flagset:       o.Flagset,
		Fetch:         o.Fetch,
		Sym:           sym,
		Obj:           obj,
		UI:            o.UI,
		HTTPServer:    httpServer,
		HTTPTransport: o.HTTPTransport,
	}
}

// HTTPServerArgs contains arguments needed by an HTTP server that
// is exporting a pprof web interface.
type HTTPServerArgs plugin.HTTPServerArgs

// Options groups all the optional plugins into pprof.
type Options struct {
	Writer        Writer
	Flagset       FlagSet
	Fetch         Fetcher
	Sym           Symbolizer
	Obj           ObjTool
	UI            UI
	HTTPServer    func(*HTTPServerArgs) error
	HTTPTransport http.RoundTripper
}

// Writer provides a mechanism to write data under a certain name,
// typically a filename.
type Writer interface {
	Open(name string) (io.WriteCloser, error)
}

// A FlagSet creates and parses command-line flags.
// It is similar to the standard flag.FlagSet.
type FlagSet interface {
	// Bool, Int, Float64, and String define new flags,
	// like the functions of the same name in package flag.
	Bool(name string, def bool, usage string) *bool
	Int(name string, def int, usage string) *int
	Float64(name string, def float64, usage string) *float64
	String(name string, def string, usage string) *string

	// StringList is similar to String but allows multiple values for a
	// single flag
	StringList(name string, def string, usage string) *[]*string

	// ExtraUsage returns any additional text that should be printed after the
	// standard usage message. The extra usage message returned includes all text
	// added with AddExtraUsage().
	// The typical use of ExtraUsage is to show any custom flags defined by the
	// specific pprof plugins being used.
	ExtraUsage() string

	// AddExtraUsage appends additional text to the end of the extra usage message.
	AddExtraUsage(eu string)

	// Parse initializes the flags with their values for this run
	// and returns the non-flag command line arguments.
	// If an unknown flag is encountered or there are no arguments,
	// Parse should call usage and return nil.
	Parse(usage func()) []string
}

// A Fetcher reads and returns the profile named by src, using
// the specified duration and timeout. It returns the fetched
// profile and a string indicating a URL from where the profile
// was fetched, which may be different than src.
type Fetcher interface {
	Fetch(src string, duration, timeout time.Duration) (*profile.Profile, string, error)
}

// A Symbolizer introduces symbol information into a profile.
type Symbolizer interface {
	Symbolize(mode string, srcs MappingSources, prof *profile.Profile) error
}

// MappingSources map each profile.Mapping to the source of the profile.
// The key is either Mapping.File or Mapping.BuildId.
type MappingSources map[string][]struct {
	Source string // URL of the source the mapping was collected from
	Start  uint64 // delta applied to addresses from this source (to represent Merge adjustments)
}

// An ObjTool inspects shared libraries and executable files.
type ObjTool interface {
	// Open opens the named object file. If the object is a shared
	// library, start/limit/offset are the addresses where it is mapped
	// into memory in the address space being inspected. If the object
	// is a linux kernel, relocationSymbol is the name of the symbol
	// corresponding to the start address.
	Open(file string, start, limit, offset uint64, relocationSymbol string) (ObjFile, error)

	// Disasm disassembles the named object file, starting at
	// the start address and stopping at (before) the end address.
	Disasm(file string, start, end uint64, intelSyntax bool) ([]Inst, error)
}

// An Inst is a single instruction in an assembly listing.
type Inst struct {
	Addr     uint64 // virtual address of instruction
	Text     string // instruction text
	Function string // function name
	File     string // source file
	Line     int    // source line
}

// An ObjFile is a single object file: a shared library or executable.
type ObjFile interface {
	// Name returns the underlying file name, if available.
	Name() string

	// ObjAddr returns the objdump address corresponding to a runtime address.
	ObjAddr(addr uint64) (uint64, error)

	// BuildID returns the GNU build ID of the file, or an empty string.
	BuildID() string

	// SourceLine reports the source line information for a given
	// address in the file. Due to inlining, the source line information
	// is in general a list of positions representing a call stack,
	// with the leaf function first.
	SourceLine(addr uint64) ([]Frame, error)

	// Symbols returns a list of symbols in the object file.
	// If r is not nil, Symbols restricts the list to symbols
	// with names matching the regular expression.
	// If addr is not zero, Symbols restricts the list to symbols
	// containing that address.
	Symbols(r *regexp.Regexp, addr uint64) ([]*Sym, error)

	// Close closes the file, releasing associated resources.
	Close() error
}

// A Frame describes a single line in a source file.
type Frame struct {
	Func      string // name of function
	File      string // source file name
	Line      int    // line in file
	Column    int    // column in file
	StartLine int    // start line of function (if available)
}

// A Sym describes a single symbol in an object file.
type Sym struct {
	Name  []string // names of symbol (many if symbol was dedup'ed)
	File  string   // object file containing symbol
	Start uint64   // start virtual address
	End   uint64   // virtual address of last byte in sym (Start+size-1)
}

// A UI manages user interactions.
type UI interface {
	// ReadLine returns a line of text (a command) read from the user.
	// prompt is printed before reading the command.
	ReadLine(prompt string) (string, error)

	// Print shows a message to the user.
	// It formats the text as fmt.Print would and adds a final \n if not already present.
	// For line-based UI, Print writes to standard error.
	// (Standard output is reserved for report data.)
	Print(...interface{})

	// PrintErr shows an error message to the user.
	// It formats the text as fmt.Print would and adds a final \n if not already present.
	// For line-based UI, PrintErr writes to standard error.
	PrintErr(...interface{})

	// IsTerminal returns whether the UI is known to be tied to an
	// interactive terminal (as opposed to being redirected to a file).
	IsTerminal() bool

	// WantBrowser indicates whether browser should be opened with the -http option.
	WantBrowser() bool

	// SetAutoComplete instructs the UI to call complete(cmd) to obtain
	// the auto-completion of cmd, if the UI supports auto-completion at all.
	SetAutoComplete(complete func(string) string)
}

// internalObjTool is a wrapper to map from the pprof external
// interface to the internal interface.
type internalObjTool struct {
	ObjTool
}

func (o *internalObjTool) Open(file string, start, limit, offset uint64, relocationSymbol string) (plugin.ObjFile, error) {
	f, err := o.ObjTool.Open(file, start, limit, offset, relocationSymbol)
	if err != nil {
		return nil, err
	}
	return &internalObjFile{f}, err
}

type internalObjFile struct {
	ObjFile
}

func (f *internalObjFile) SourceLine(frame uint64) ([]plugin.Frame, error) {
	frames, err := f.ObjFile.SourceLine(frame)
	if err != nil {
		return nil, err
	}
	var pluginFrames []plugin.Frame
	for _, f := range frames {
		pluginFrames = append(pluginFrames, plugin.Frame(f))
	}
	return pluginFrames, nil
}

func (f *internalObjFile) Symbols(r *regexp.Regexp, addr uint64) ([]*plugin.Sym, error) {
	syms, err := f.ObjFile.Symbols(r, addr)
	if err != nil {
		return nil, err
	}
	var pluginSyms []*plugin.Sym
	for _, s := range syms {
		ps := plugin.Sym(*s)
		pluginSyms = append(pluginSyms, &ps)
	}
	return pluginSyms, nil
}

func (o *internalObjTool) Disasm(file string, start, end uint64, intelSyntax bool) ([]plugin.Inst, error) {
	insts, err := o.ObjTool.Disasm(file, start, end, intelSyntax)
	if err != nil {
		return nil, err
	}
	var pluginInst []plugin.Inst
	for _, inst := range insts {
		pluginInst = append(pluginInst, plugin.Inst(inst))
	}
	return pluginInst, nil
}

// internalSymbolizer is a wrapper to map from the pprof external
// interface to the internal interface.
type internalSymbolizer struct {
	Symbolizer
}

func (s *internalSymbolizer) Symbolize(mode string, srcs plugin.MappingSources, prof *profile.Profile) error {
	isrcs := MappingSources{}
	maps.Copy(isrcs, srcs)
	return s.Symbolizer.Symbolize(mode, isrcs, prof)
}


================================================
FILE: fuzz/README.md
================================================
This is an explanation of how to do fuzzing of ParseData. This uses github.com/dvyukov/go-fuzz/ for fuzzing.

# How to use
First, get go-fuzz 
```
$ go get github.com/dvyukov/go-fuzz/go-fuzz
$ go get github.com/dvyukov/go-fuzz/go-fuzz-build
```

Build the test program by calling the following command 
(assuming you have files for pprof located in github.com/google/pprof within go's src folder)

```
$ go-fuzz-build github.com/google/pprof/fuzz
```
The above command will produce pprof-fuzz.zip 


Now you can run the fuzzer by calling

```
$ go-fuzz -bin=./pprof-fuzz.zip -workdir=fuzz
```

This will save a corpus of files used by the fuzzer in ./fuzz/corpus, and
all files that caused ParseData to crash in ./fuzz/crashers.

For more details on the usage, see github.com/dvyukov/go-fuzz/

# About the to corpus

Right now, fuzz/corpus contains the corpus initially given to the fuzzer

If using the above commands, fuzz/corpus will be used to generate the initial corpus during fuzz testing.

One can add profiles into the corpus by placing these files in the corpus directory (fuzz/corpus)
prior to calling go-fuzz-build.


================================================
FILE: fuzz/corpus/empty
================================================


================================================
FILE: fuzz/fuzz_test.go
================================================
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package pprof

import (
	"os"
	"runtime"
	"testing"

	"github.com/google/pprof/profile"
)

func TestParseData(t *testing.T) {
	if runtime.GOOS == "nacl" {
		t.Skip("no direct filesystem access on Nacl")
	}

	const path = "testdata/"
	files, err := os.ReadDir(path)
	if err != nil {
		t.Errorf("Problem reading directory %s : %v", path, err)
	}
	for _, f := range files {
		file := path + f.Name()
		inbytes, err := os.ReadFile(file)
		if err != nil {
			t.Errorf("Problem reading file: %s : %v", file, err)
			continue
		}
		profile.ParseData(inbytes)
	}
}


================================================
FILE: fuzz/main.go
================================================
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package pprof is used in conjunction with github.com/dvyukov/go-fuzz/go-fuzz
// to fuzz ParseData function.
package pprof

import (
	"github.com/google/pprof/profile"
)

// Fuzz can be used with https://github.com/dvyukov/go-fuzz to do fuzz testing on ParseData
func Fuzz(data []byte) int {
	profile.ParseData(data)
	return 0
}


================================================
FILE: fuzz/testdata/7e3c92482f6f39fc502b822ded792c589849cca8
================================================
--- heapz 1 ---
0 0 @ 0


================================================
FILE: go.mod
================================================
module github.com/google/pprof

go 1.24.0

toolchain go1.24.9

require (
	github.com/chzyer/readline v1.5.1
	github.com/ianlancetaylor/demangle v0.0.0-20250417193237-f615e6bd150b
)

require golang.org/x/sys v0.32.0 // indirect


================================================
FILE: go.sum
================================================
github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
github.com/ianlancetaylor/demangle v0.0.0-20250417193237-f615e6bd150b h1:ogbOPx86mIhFy764gGkqnkFC8m5PJA7sPzlk9ppLVQA=
github.com/ianlancetaylor/demangle v0.0.0-20250417193237-f615e6bd150b/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=


================================================
FILE: internal/binutils/addr2liner.go
================================================
// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package binutils

import (
	"bufio"
	"fmt"
	"io"
	"os/exec"
	"strconv"
	"strings"
	"sync"

	"github.com/google/pprof/internal/plugin"
)

const (
	defaultAddr2line = "addr2line"

	// addr2line may produce multiple lines of output. We
	// use this sentinel to identify the end of the output.
	sentinel = ^uint64(0)
)

// addr2Liner is a connection to an addr2line command for obtaining
// address and line number information from a binary.
type addr2Liner struct {
	mu   sync.Mutex
	rw   lineReaderWriter
	base uint64

	// nm holds an addr2Liner using nm tool. Certain versions of addr2line
	// produce incomplete names due to
	// https://sourceware.org/bugzilla/show_bug.cgi?id=17541. As a workaround,
	// the names from nm are used when they look more complete. See addrInfo()
	// code below for the exact heuristic.
	nm *addr2LinerNM
}

// lineReaderWriter is an interface to abstract the I/O to an addr2line
// process. It writes a line of input to the job, and reads its output
// one line at a time.
type lineReaderWriter interface {
	write(string) error
	readLine() (string, error)
	close()
}

type addr2LinerJob struct {
	cmd *exec.Cmd
	in  io.WriteCloser
	out *bufio.Reader
}

func (a *addr2LinerJob) write(s string) error {
	_, err := fmt.Fprint(a.in, s+"\n")
	return err
}

func (a *addr2LinerJob) readLine() (string, error) {
	s, err := a.out.ReadString('\n')
	if err != nil {
		return "", err
	}
	return strings.TrimSpace(s), nil
}

// close releases any resources used by the addr2liner object.
func (a *addr2LinerJob) close() {
	a.in.Close()
	a.cmd.Wait()
}

// newAddr2Liner starts the given addr2liner command reporting
// information about the given executable file. If file is a shared
// library, base should be the address at which it was mapped in the
// program under consideration.
func newAddr2Liner(cmd, file string, base uint64) (*addr2Liner, error) {
	if cmd == "" {
		cmd = defaultAddr2line
	}

	j := &addr2LinerJob{
		cmd: exec.Command(cmd, "-aif", "-e", file),
	}

	var err error
	if j.in, err = j.cmd.StdinPipe(); err != nil {
		return nil, err
	}

	outPipe, err := j.cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}

	j.out = bufio.NewReader(outPipe)
	if err := j.cmd.Start(); err != nil {
		return nil, err
	}

	a := &addr2Liner{
		rw:   j,
		base: base,
	}

	return a, nil
}

// readFrame parses the addr2line output for a single address. It
// returns a populated plugin.Frame and whether it has reached the end of the
// data.
func (d *addr2Liner) readFrame() (plugin.Frame, bool) {
	funcname, err := d.rw.readLine()
	if err != nil {
		return plugin.Frame{}, true
	}
	if strings.HasPrefix(funcname, "0x") {
		// If addr2line returns a hex address we can assume it is the
		// sentinel. Read and ignore next two lines of output from
		// addr2line
		d.rw.readLine()
		d.rw.readLine()
		return plugin.Frame{}, true
	}

	fileline, err := d.rw.readLine()
	if err != nil {
		return plugin.Frame{}, true
	}

	linenumber := 0

	if funcname == "??" {
		funcname = ""
	}

	if fileline == "??:0" {
		fileline = ""
	} else {
		if i := strings.LastIndex(fileline, ":"); i >= 0 {
			// Remove discriminator, if present
			if disc := strings.Index(fileline, " (discriminator"); disc > 0 {
				fileline = fileline[:disc]
			}
			// If we cannot parse a number after the last ":", keep it as
			// part of the filename.
			if line, err := strconv.Atoi(fileline[i+1:]); err == nil {
				linenumber = line
				fileline = fileline[:i]
			}
		}
	}

	return plugin.Frame{
		Func: funcname,
		File: fileline,
		Line: linenumber}, false
}

func (d *addr2Liner) rawAddrInfo(addr uint64) ([]plugin.Frame, error) {
	d.mu.Lock()
	defer d.mu.Unlock()

	if err := d.rw.write(fmt.Sprintf("%x", addr-d.base)); err != nil {
		return nil, err
	}

	if err := d.rw.write(fmt.Sprintf("%x", sentinel)); err != nil {
		return nil, err
	}

	resp, err := d.rw.readLine()
	if err != nil {
		return nil, err
	}

	if !strings.HasPrefix(resp, "0x") {
		return nil, fmt.Errorf("unexpected addr2line output: %s", resp)
	}

	var stack []plugin.Frame
	for {
		frame, end := d.readFrame()
		if end {
			break
		}

		if frame != (plugin.Frame{}) {
			stack = append(stack, frame)
		}
	}
	return stack, err
}

// addrInfo returns the stack frame information for a specific program
// address. It returns nil if the address could not be identified.
func (d *addr2Liner) addrInfo(addr uint64) ([]plugin.Frame, error) {
	stack, err := d.rawAddrInfo(addr)
	if err != nil {
		return nil, err
	}

	// Certain versions of addr2line produce incomplete names due to
	// https://sourceware.org/bugzilla/show_bug.cgi?id=17541. Attempt to replace
	// the name with a better one from nm.
	if len(stack) > 0 && d.nm != nil {
		nm, err := d.nm.addrInfo(addr)
		if err == nil && len(nm) > 0 {
			// Last entry in frame list should match since it is non-inlined. As a
			// simple heuristic, we only switch to the nm-based name if it is longer
			// by 2 or more characters. We consider nm names that are longer by 1
			// character insignificant to avoid replacing foo with _foo on MacOS (for
			// unknown reasons read2line produces the former and nm produces the
			// latter on MacOS even though both tools are asked to produce mangled
			// names).
			nmName := nm[len(nm)-1].Func
			a2lName := stack[len(stack)-1].Func
			if len(nmName) > len(a2lName)+1 {
				stack[len(stack)-1].Func = nmName
			}
		}
	}

	return stack, nil
}


================================================
FILE: internal/binutils/addr2liner_llvm.go
================================================
// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package binutils

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io"
	"os/exec"
	"strconv"
	"strings"
	"sync"

	"github.com/google/pprof/internal/plugin"
)

const (
	defaultLLVMSymbolizer = "llvm-symbolizer"
)

// llvmSymbolizer is a connection to an llvm-symbolizer command for
// obtaining address and line number information from a binary.
type llvmSymbolizer struct {
	sync.Mutex
	filename string
	rw       lineReaderWriter
	base     uint64
	isData   bool
}

type llvmSymbolizerJob struct {
	cmd *exec.Cmd
	in  io.WriteCloser
	out *bufio.Reader
	// llvm-symbolizer requires the symbol type, CODE or DATA, for symbolization.
	symType string
}

func (a *llvmSymbolizerJob) write(s string) error {
	_, err := fmt.Fprintln(a.in, a.symType, s)
	return err
}

func (a *llvmSymbolizerJob) readLine() (string, error) {
	s, err := a.out.ReadString('\n')
	if err != nil {
		return "", err
	}
	return strings.TrimSpace(s), nil
}

// close releases any resources used by the llvmSymbolizer object.
func (a *llvmSymbolizerJob) close() {
	a.in.Close()
	a.cmd.Wait()
}

// newLLVMSymbolizer starts the given llvmSymbolizer command reporting
// information about the given executable file. If file is a shared
// library, base should be the address at which it was mapped in the
// program under consideration.
func newLLVMSymbolizer(cmd, file string, base uint64, isData bool) (*llvmSymbolizer, error) {
	if cmd == "" {
		cmd = defaultLLVMSymbolizer
	}

	j := &llvmSymbolizerJob{
		cmd:     exec.Command(cmd, "--inlining", "-demangle=false", "--output-style=JSON"),
		symType: "CODE",
	}
	if isData {
		j.symType = "DATA"
	}

	var err error
	if j.in, err = j.cmd.StdinPipe(); err != nil {
		return nil, err
	}

	outPipe, err := j.cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}

	j.out = bufio.NewReader(outPipe)
	if err := j.cmd.Start(); err != nil {
		return nil, err
	}

	a := &llvmSymbolizer{
		filename: file,
		rw:       j,
		base:     base,
		isData:   isData,
	}

	return a, nil
}

// readDataFrames parses the llvm-symbolizer DATA output for a single address. It
// returns a populated plugin.Frame array with a single entry.
func (d *llvmSymbolizer) readDataFrames() ([]plugin.Frame, error) {
	line, err := d.rw.readLine()
	if err != nil {
		return nil, err
	}
	var frame struct {
		Address    string `json:"Address"`
		ModuleName string `json:"ModuleName"`
		Data       struct {
			Start string `json:"Start"`
			Size  string `json:"Size"`
			Name  string `json:"Name"`
		} `json:"Data"`
	}
	if err := json.Unmarshal([]byte(line), &frame); err != nil {
		return nil, err
	}
	// Match non-JSON output behaviour of stuffing the start/size into the filename of a single frame,
	// with the size being a decimal value.
	size, err := strconv.ParseInt(frame.Data.Size, 0, 0)
	if err != nil {
		return nil, err
	}
	var stack []plugin.Frame
	stack = append(stack, plugin.Frame{Func: frame.Data.Name, File: fmt.Sprintf("%s %d", frame.Data.Start, size)})
	return stack, nil
}

// readCodeFrames parses the llvm-symbolizer CODE output for a single address. It
// returns a populated plugin.Frame array.
func (d *llvmSymbolizer) readCodeFrames() ([]plugin.Frame, error) {
	line, err := d.rw.readLine()
	if err != nil {
		return nil, err
	}
	var frame struct {
		Address    string `json:"Address"`
		ModuleName string `json:"ModuleName"`
		Symbol     []struct {
			Line         int    `json:"Line"`
			Column       int    `json:"Column"`
			FunctionName string `json:"FunctionName"`
			FileName     string `json:"FileName"`
			StartLine    int    `json:"StartLine"`
		} `json:"Symbol"`
	}
	if err := json.Unmarshal([]byte(line), &frame); err != nil {
		return nil, err
	}
	var stack []plugin.Frame
	for _, s := range frame.Symbol {
		stack = append(stack, plugin.Frame{Func: s.FunctionName, File: s.FileName, Line: s.Line, Column: s.Column, StartLine: s.StartLine})
	}
	return stack, nil
}

// addrInfo returns the stack frame information for a specific program
// address. It returns nil if the address could not be identified.
func (d *llvmSymbolizer) addrInfo(addr uint64) ([]plugin.Frame, error) {
	d.Lock()
	defer d.Unlock()

	if err := d.rw.write(fmt.Sprintf("%s 0x%x", d.filename, addr-d.base)); err != nil {
		return nil, err
	}
	if d.isData {
		return d.readDataFrames()
	}
	return d.readCodeFrames()
}


================================================
FILE: internal/binutils/addr2liner_nm.go
================================================
// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package binutils

import (
	"bufio"
	"bytes"
	"io"
	"os/exec"
	"strconv"
	"strings"

	"github.com/google/pprof/internal/plugin"
)

const (
	defaultNM = "nm"
)

// addr2LinerNM is a connection to an nm command for obtaining symbol
// information from a binary.
type addr2LinerNM struct {
	m []symbolInfo // Sorted list of symbol addresses from binary.
}

type symbolInfo struct {
	address uint64
	size    uint64
	name    string
	symType string
}

// isData returns if the symbol has a known data object symbol type.
func (s *symbolInfo) isData() bool {
	// The following symbol types are taken from https://linux.die.net/man/1/nm:
	// Lowercase letter means local symbol, uppercase denotes a global symbol.
	// - b or B: the symbol is in the uninitialized data section, e.g. .bss;
	// - d or D: the symbol is in the initialized data section;
	// - r or R: the symbol is in a read only data section;
	// - v or V: the symbol is a weak object;
	// - W: the symbol is a weak symbol that has not been specifically tagged as a
	//      weak object symbol. Experiments with some binaries, showed these to be
	//      mostly data objects.
	return strings.ContainsAny(s.symType, "bBdDrRvVW")
}

// newAddr2LinerNM starts the given nm command reporting information about the
// given executable file. If file is a shared library, base should be the
// address at which it was mapped in the program under consideration.
func newAddr2LinerNM(cmd, file string, base uint64) (*addr2LinerNM, error) {
	if cmd == "" {
		cmd = defaultNM
	}
	var b bytes.Buffer
	c := exec.Command(cmd, "--numeric-sort", "--print-size", "--format=posix", file)
	c.Stdout = &b
	if err := c.Run(); err != nil {
		return nil, err
	}
	return parseAddr2LinerNM(base, &b)
}

func parseAddr2LinerNM(base uint64, nm io.Reader) (*addr2LinerNM, error) {
	a := &addr2LinerNM{
		m: []symbolInfo{},
	}

	// Parse nm output and populate symbol map.
	// Skip lines we fail to parse.
	buf := bufio.NewReader(nm)
	for {
		line, err := buf.ReadString('\n')
		if line == "" && err != nil {
			if err == io.EOF {
				break
			}
			return nil, err
		}
		line = strings.TrimSpace(line)
		fields := strings.Split(line, " ")
		if len(fields) != 4 {
			continue
		}
		address, err := strconv.ParseUint(fields[2], 16, 64)
		if err != nil {
			continue
		}
		size, err := strconv.ParseUint(fields[3], 16, 64)
		if err != nil {
			continue
		}
		a.m = append(a.m, symbolInfo{
			address: address + base,
			size:    size,
			name:    fields[0],
			symType: fields[1],
		})
	}

	return a, nil
}

// addrInfo returns the stack frame information for a specific program
// address. It returns nil if the address could not be identified.
func (a *addr2LinerNM) addrInfo(addr uint64) ([]plugin.Frame, error) {
	if len(a.m) == 0 || addr < a.m[0].address || addr >= (a.m[len(a.m)-1].address+a.m[len(a.m)-1].size) {
		return nil, nil
	}

	// Binary search. Search until low, high are separated by 1.
	low, high := 0, len(a.m)
	for low+1 < high {
		mid := (low + high) / 2
		v := a.m[mid].address
		if addr == v {
			low = mid
			break
		} else if addr > v {
			low = mid
		} else {
			high = mid
		}
	}

	// Address is between a.m[low] and a.m[high]. Pick low, as it represents
	// [low, high). For data symbols, we use a strict check that the address is in
	// the [start, start + size) range of a.m[low].
	if a.m[low].isData() && addr >= (a.m[low].address+a.m[low].size) {
		return nil, nil
	}
	return []plugin.Frame{{Func: a.m[low].name}}, nil
}


================================================
FILE: internal/binutils/binutils.go
================================================
// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package binutils provides access to the GNU binutils.
package binutils

import (
	"debug/elf"
	"debug/macho"
	"debug/pe"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"sync"

	"github.com/google/pprof/internal/elfexec"
	"github.com/google/pprof/internal/plugin"
)

// A Binutils implements plugin.ObjTool by invoking the GNU binutils.
type Binutils struct {
	mu  sync.Mutex
	rep *binrep
}

var (
	objdumpLLVMVerRE = regexp.MustCompile(`LLVM version (?:(\d*)\.(\d*)\.(\d*)|.*(trunk).*)`)

	// Defined for testing
	elfOpen = elf.Open
)

// binrep is an immutable representation for Binutils.  It is atomically
// replaced on every mutation to provide thread-safe access.
type binrep struct {
	// Commands to invoke.
	llvmSymbolizer      string
	llvmSymbolizerFound bool
	addr2line           string
	addr2lineFound      bool
	nm                  string
	nmFound             bool
	objdump             string
	objdumpFound        bool
	isLLVMObjdump       bool

	// if fast, perform symbolization using nm (symbol names only),
	// instead of file-line detail from the slower addr2line.
	fast bool
}

// get returns the current representation for bu, initializing it if necessary.
func (bu *Binutils) get() *binrep {
	bu.mu.Lock()
	r := bu.rep
	if r == nil {
		r = &binrep{}
		initTools(r, "")
		bu.rep = r
	}
	bu.mu.Unlock()
	return r
}

// update modifies the rep for bu via the supplied function.
func (bu *Binutils) update(fn func(r *binrep)) {
	r := &binrep{}
	bu.mu.Lock()
	defer bu.mu.Unlock()
	if bu.rep == nil {
		initTools(r, "")
	} else {
		*r = *bu.rep
	}
	fn(r)
	bu.rep = r
}

// String returns string representation of the binutils state for debug logging.
func (bu *Binutils) String() string {
	r := bu.get()
	var llvmSymbolizer, addr2line, nm, objdump string
	if r.llvmSymbolizerFound {
		llvmSymbolizer = r.llvmSymbolizer
	}
	if r.addr2lineFound {
		addr2line = r.addr2line
	}
	if r.nmFound {
		nm = r.nm
	}
	if r.objdumpFound {
		objdump = r.objdump
	}
	return fmt.Sprintf("llvm-symbolizer=%q addr2line=%q nm=%q objdump=%q fast=%t",
		llvmSymbolizer, addr2line, nm, objdump, r.fast)
}

// SetFastSymbolization sets a toggle that makes binutils use fast
// symbolization (using nm), which is much faster than addr2line but
// provides only symbol name information (no file/line).
func (bu *Binutils) SetFastSymbolization(fast bool) {
	bu.update(func(r *binrep) { r.fast = fast })
}

// SetTools processes the contents of the tools option. It
// expects a set of entries separated by commas; each entry is a pair
// of the form t:path, where cmd will be used to look only for the
// tool named t. If t is not specified, the path is searched for all
// tools.
func (bu *Binutils) SetTools(config string) {
	bu.update(func(r *binrep) { initTools(r, config) })
}

func initTools(b *binrep, config string) {
	// paths collect paths per tool; Key "" contains the default.
	paths := make(map[string][]string)
	for _, t := range strings.Split(config, ",") {
		name, path := "", t
		if ct := strings.SplitN(t, ":", 2); len(ct) == 2 {
			name, path = ct[0], ct[1]
		}
		paths[name] = append(paths[name], path)
	}

	defaultPath := paths[""]
	b.llvmSymbolizer, b.llvmSymbolizerFound = chooseExe([]string{"llvm-symbolizer"}, []string{}, append(paths["llvm-symbolizer"], defaultPath...))
	b.addr2line, b.addr2lineFound = chooseExe([]string{"addr2line"}, []string{"gaddr2line"}, append(paths["addr2line"], defaultPath...))
	// The "-n" option is supported by LLVM since 2011. The output of llvm-nm
	// and GNU nm with "-n" option is interchangeable for our purposes, so we do
	// not need to differrentiate them.
	b.nm, b.nmFound = chooseExe([]string{"llvm-nm", "nm"}, []string{"gnm"}, append(paths["nm"], defaultPath...))
	b.objdump, b.objdumpFound, b.isLLVMObjdump = findObjdump(append(paths["objdump"], defaultPath...))
}

// findObjdump finds and returns path to preferred objdump binary.
// Order of preference is: llvm-objdump, objdump.
// On MacOS only, also looks for gobjdump with least preference.
// Accepts a list of paths and returns:
// a string with path to the preferred objdump binary if found,
// or an empty string if not found;
// a boolean if any acceptable objdump was found;
// a boolean indicating if it is an LLVM objdump.
func findObjdump(paths []string) (string, bool, bool) {
	objdumpNames := []string{"llvm-objdump", "objdump"}
	if runtime.GOOS == "darwin" {
		objdumpNames = append(objdumpNames, "gobjdump")
	}

	for _, objdumpName := range objdumpNames {
		if objdump, objdumpFound := findExe(objdumpName, paths); objdumpFound {
			cmdOut, err := exec.Command(objdump, "--version").Output()
			if err != nil {
				continue
			}
			if isLLVMObjdump(string(cmdOut)) {
				return objdump, true, true
			}
			if isBuObjdump(string(cmdOut)) {
				return objdump, true, false
			}
		}
	}
	return "", false, false
}

// chooseExe finds and returns path to preferred binary. names is a list of
// names to search on both Linux and OSX. osxNames is a list of names specific
// to OSX. names always has a higher priority than osxNames. The order of
// the name within each list decides its priority (e.g. the first name has a
// higher priority than the second name in the list).
//
// It returns a string with path to the binary and a boolean indicating if any
// acceptable binary was found.
func chooseExe(names, osxNames []string, paths []string) (string, bool) {
	if runtime.GOOS == "darwin" {
		names = append(names, osxNames...)
	}
	for _, name := range names {
		if binary, found := findExe(name, paths); found {
			return binary, true
		}
	}
	return "", false
}

// isLLVMObjdump accepts a string with path to an objdump binary,
// and returns a boolean indicating if the given binary is an LLVM
// objdump binary of an acceptable version.
func isLLVMObjdump(output string) bool {
	fields := objdumpLLVMVerRE.FindStringSubmatch(output)
	if len(fields) != 5 {
		return false
	}
	if fields[4] == "trunk" {
		return true
	}
	verMajor, err := strconv.Atoi(fields[1])
	if err != nil {
		return false
	}
	verPatch, err := strconv.Atoi(fields[3])
	if err != nil {
		return false
	}
	if runtime.GOOS == "linux" && verMajor >= 8 {
		// Ensure LLVM objdump is at least version 8.0 on Linux.
		// Some flags, like --demangle, and double dashes for options are
		// not supported by previous versions.
		return true
	}
	if runtime.GOOS == "darwin" {
		// Ensure LLVM objdump is at least version 10.0.1 on MacOS.
		return verMajor > 10 || (verMajor == 10 && verPatch >= 1)
	}
	return false
}

// isBuObjdump accepts a string with path to an objdump binary,
// and returns a boolean indicating if the given binary is a GNU
// binutils objdump binary. No version check is performed.
func isBuObjdump(output string) bool {
	return strings.Contains(output, "GNU objdump")
}

// findExe looks for an executable command on a set of paths.
// If it cannot find it, returns cmd.
func findExe(cmd string, paths []string) (string, bool) {
	for _, p := range paths {
		cp := filepath.Join(p, cmd)
		if c, err := exec.LookPath(cp); err == nil {
			return c, true
		}
	}
	return cmd, false
}

// Disasm returns the assembly instructions for the specified address range
// of a binary.
func (bu *Binutils) Disasm(file string, start, end uint64, intelSyntax bool) ([]plugin.Inst, error) {
	b := bu.get()
	if !b.objdumpFound {
		return nil, errors.New("cannot disasm: no objdump tool available")
	}
	args := []string{"--disassemble", "--demangle", "--no-show-raw-insn",
		"--line-numbers", fmt.Sprintf("--start-address=%#x", start),
		fmt.Sprintf("--stop-address=%#x", end)}

	if intelSyntax {
		if b.isLLVMObjdump {
			args = append(args, "--x86-asm-syntax=intel")
		} else {
			args = append(args, "-M", "intel")
		}
	}

	args = append(args, file)
	cmd := exec.Command(b.objdump, args...)
	out, err := cmd.Output()
	if err != nil {
		return nil, fmt.Errorf("%v: %v", cmd.Args, err)
	}

	return disassemble(out)
}

// Open satisfies the plugin.ObjTool interface.
func (bu *Binutils) Open(name string, start, limit, offset uint64, relocationSymbol string) (plugin.ObjFile, error) {
	b := bu.get()

	// Make sure file is a supported executable.
	// This uses magic numbers, mainly to provide better error messages but
	// it should also help speed.

	if _, err := os.Stat(name); err != nil {
		// For testing, do not require file name to exist.
		if strings.Contains(b.addr2line, "testdata/") {
			return &fileAddr2Line{file: file{b: b, name: name}}, nil
		}
		return nil, err
	}

	// Read the first 4 bytes of the file.

	f, err := os.Open(name)
	if err != nil {
		return nil, fmt.Errorf("error opening %s: %v", name, err)
	}
	defer f.Close()

	var header [4]byte
	if _, err = io.ReadFull(f, header[:]); err != nil {
		return nil, fmt.Errorf("error reading magic number from %s: %v", name, err)
	}

	elfMagic := string(header[:])

	// Match against supported file types.
	if elfMagic == elf.ELFMAG {
		f, err := b.openELF(name, start, limit, offset, relocationSymbol)
		if err != nil {
			return nil, fmt.Errorf("error reading ELF file %s: %v", name, err)
		}
		return f, nil
	}

	// Mach-O magic numbers can be big or little endian.
	machoMagicLittle := binary.LittleEndian.Uint32(header[:])
	machoMagicBig := binary.BigEndian.Uint32(header[:])

	if machoMagicLittle == macho.Magic32 || machoMagicLittle == macho.Magic64 ||
		machoMagicBig == macho.Magic32 || machoMagicBig == macho.Magic64 {
		f, err := b.openMachO(name, start, limit, offset)
		if err != nil {
			return nil, fmt.Errorf("error reading Mach-O file %s: %v", name, err)
		}
		return f, nil
	}
	if machoMagicLittle == macho.MagicFat || machoMagicBig == macho.MagicFat {
		f, err := b.openFatMachO(name, start, limit, offset)
		if err != nil {
			return nil, fmt.Errorf("error reading fat Mach-O file %s: %v", name, err)
		}
		return f, nil
	}

	peMagic := string(header[:2])
	if peMagic == "MZ" {
		f, err := b.openPE(name, start, limit, offset)
		if err != nil {
			return nil, fmt.Errorf("error reading PE file %s: %v", name, err)
		}
		return f, nil
	}

	return nil, fmt.Errorf("unrecognized binary format: %s", name)
}

func (b *binrep) openMachOCommon(name string, of *macho.File, start, limit, offset uint64) (plugin.ObjFile, error) {

	// Subtract the load address of the __TEXT section. Usually 0 for shared
	// libraries or 0x100000000 for executables. You can check this value by
	// running `objdump -private-headers <file>`.

	textSegment := of.Segment("__TEXT")
	if textSegment == nil {
		return nil, fmt.Errorf("could not identify base for %s: no __TEXT segment", name)
	}
	if textSegment.Addr > start {
		return nil, fmt.Errorf("could not identify base for %s: __TEXT segment address (0x%x) > mapping start address (0x%x)",
			name, textSegment.Addr, start)
	}

	base := start - textSegment.Addr

	if b.fast || (!b.addr2lineFound && !b.llvmSymbolizerFound) {
		return &fileNM{file: file{b: b, name: name, base: base}}, nil
	}
	return &fileAddr2Line{file: file{b: b, name: name, base: base}}, nil
}

func (b *binrep) openFatMachO(name string, start, limit, offset uint64) (plugin.ObjFile, error) {
	of, err := macho.OpenFat(name)
	if err != nil {
		return nil, fmt.Errorf("error parsing %s: %v", name, err)
	}
	defer of.Close()

	if len(of.Arches) == 0 {
		return nil, fmt.Errorf("empty fat Mach-O file: %s", name)
	}

	var arch macho.Cpu
	// Use the host architecture.
	// TODO: This is not ideal because the host architecture may not be the one
	// that was profiled. E.g. an amd64 host can profile a 386 program.
	switch runtime.GOARCH {
	case "386":
		arch = macho.Cpu386
	case "amd64", "amd64p32":
		arch = macho.CpuAmd64
	case "arm", "armbe", "arm64", "arm64be":
		arch = macho.CpuArm
	case "ppc":
		arch = macho.CpuPpc
	case "ppc64", "ppc64le":
		arch = macho.CpuPpc64
	default:
		return nil, fmt.Errorf("unsupported host architecture for %s: %s", name, runtime.GOARCH)
	}
	for i := range of.Arches {
		if of.Arches[i].Cpu == arch {
			return b.openMachOCommon(name, of.Arches[i].File, start, limit, offset)
		}
	}
	return nil, fmt.Errorf("architecture not found in %s: %s", name, runtime.GOARCH)
}

func (b *binrep) openMachO(name string, start, limit, offset uint64) (plugin.ObjFile, error) {
	of, err := macho.Open(name)
	if err != nil {
		return nil, fmt.Errorf("error parsing %s: %v", name, err)
	}
	defer of.Close()

	return b.openMachOCommon(name, of, start, limit, offset)
}

func (b *binrep) openELF(name string, start, limit, offset uint64, relocationSymbol string) (plugin.ObjFile, error) {
	ef, err := elfOpen(name)
	if err != nil {
		return nil, fmt.Errorf("error parsing %s: %v", name, err)
	}
	defer ef.Close()

	buildID := ""
	if id, err := elfexec.GetBuildID(ef); err == nil {
		buildID = fmt.Sprintf("%x", id)
	}

	var (
		kernelOffset *uint64
		pageAligned  = func(addr uint64) bool { return addr%4096 == 0 }
	)
	if strings.Contains(name, "vmlinux") || !pageAligned(start) || !pageAligned(limit) || !pageAligned(offset) {
		// Reading all Symbols is expensive, and we only rarely need it so
		// we don't want to do it every time. But if _stext happens to be
		// page-aligned but isn't the same as Vaddr, we would symbolize
		// wrong. So if the name the addresses aren't page aligned, or if
		// the name is "vmlinux" we read _stext. We can be wrong if: (1)
		// someone passes a kernel path that doesn't contain "vmlinux" AND
		// (2) _stext is page-aligned AND (3) _stext is not at Vaddr
		symbols, err := ef.Symbols()
		if err != nil && err != elf.ErrNoSymbols {
			return nil, err
		}

		// The kernel relocation symbol (the mapping start address) can be either
		// _text or _stext. When profiles are generated by `perf`, which one was used is
		// distinguished by the mapping name for the kernel image:
		// '[kernel.kallsyms]_text' or '[kernel.kallsyms]_stext', respectively. If we haven't
		// been able to parse it from the mapping, we default to _stext.
		if relocationSymbol == "" {
			relocationSymbol = "_stext"
		}
		for _, s := range symbols {
			if s.Name == relocationSymbol {
				kernelOffset = &s.Value
				break
			}
		}
	}

	// Check that we can compute a base for the binary. This may not be the
	// correct base value, so we don't save it. We delay computing the actual base
	// value until we have a sample address for this mapping, so that we can
	// correctly identify the associated program segment that is needed to compute
	// the base.
	if _, err := elfexec.GetBase(&ef.FileHeader, elfexec.FindTextProgHeader(ef), kernelOffset, start, limit, offset); err != nil {
		return nil, fmt.Errorf("could not identify base for %s: %v", name, err)
	}

	if b.fast || (!b.addr2lineFound && !b.llvmSymbolizerFound) {
		return &fileNM{file: file{
			b:       b,
			name:    name,
			buildID: buildID,
			m:       &elfMapping{start: start, limit: limit, offset: offset, kernelOffset: kernelOffset},
		}}, nil
	}
	return &fileAddr2Line{file: file{
		b:       b,
		name:    name,
		buildID: buildID,
		m:       &elfMapping{start: start, limit: limit, offset: offset, kernelOffset: kernelOffset},
	}}, nil
}

func (b *binrep) openPE(name string, start, limit, offset uint64) (plugin.ObjFile, error) {
	pf, err := pe.Open(name)
	if err != nil {
		return nil, fmt.Errorf("error parsing %s: %v", name, err)
	}
	defer pf.Close()

	var imageBase uint64
	switch h := pf.OptionalHeader.(type) {
	case *pe.OptionalHeader32:
		imageBase = uint64(h.ImageBase)
	case *pe.OptionalHeader64:
		imageBase = uint64(h.ImageBase)
	default:
		return nil, fmt.Errorf("unknown OptionalHeader %T", pf.OptionalHeader)
	}

	var base uint64
	if start > 0 {
		base = start - imageBase
	}
	if b.fast || (!b.addr2lineFound && !b.llvmSymbolizerFound) {
		return &fileNM{file: file{b: b, name: name, base: base}}, nil
	}
	return &fileAddr2Line{file: file{b: b, name: name, base: base}}, nil
}

// elfMapping stores the parameters of a runtime mapping that are needed to
// identify the ELF segment associated with a mapping.
type elfMapping struct {
	// Runtime mapping parameters.
	start, limit, offset uint64
	// Offset of kernel relocation symbol. Only defined for kernel images, nil otherwise.
	kernelOffset *uint64
}

// findProgramHeader returns the program segment that matches the current
// mapping and the given address, or an error if it cannot find a unique program
// header.
func (m *elfMapping) findProgramHeader(ef *elf.File, addr uint64) (*elf.ProgHeader, error) {
	// For user space executables, we try to find the actual program segment that
	// is associated with the given mapping. Skip this search if limit <= start.
	// We cannot use just a check on the start address of the mapping to tell if
	// it's a kernel / .ko module mapping, because with quipper address remapping
	// enabled, the address would be in the lower half of the address space.

	if m.kernelOffset != nil || m.start >= m.limit || m.limit >= (uint64(1)<<63) {
		// For the kernel, find the program segment that includes the .text section.
		return elfexec.FindTextProgHeader(ef), nil
	}

	// Fetch all the loadable segments.
	var phdrs []elf.ProgHeader
	for i := range ef.Progs {
		if ef.Progs[i].Type == elf.PT_LOAD {
			phdrs = append(phdrs, ef.Progs[i].ProgHeader)
		}
	}
	// Some ELF files don't contain any loadable program segments, e.g. .ko
	// kernel modules. It's not an error to have no header in such cases.
	if len(phdrs) == 0 {
		return nil, nil
	}
	// Get all program headers associated with the mapping.
	headers := elfexec.ProgramHeadersForMapping(phdrs, m.offset, m.limit-m.start)
	if len(headers) == 0 {
		return nil, errors.New("no program header matches mapping info")
	}
	if len(headers) == 1 {
		return headers[0], nil
	}

	// Use the file offset corresponding to the address to symbolize, to narrow
	// down the header.
	return elfexec.HeaderForFileOffset(headers, addr-m.start+m.offset)
}

// file implements the binutils.ObjFile interface.
type file struct {
	b       *binrep
	name    string
	buildID string

	baseOnce sync.Once // Ensures the base, baseErr and isData are computed once.
	base     uint64
	baseErr  error // Any eventual error while computing the base.
	isData   bool
	// Mapping information. Relevant only for ELF files, nil otherwise.
	m *elfMapping
}

// computeBase computes the relocation base for the given binary file only if
// the elfMapping field is set. It populates the base and isData fields and
// returns an error.
func (f *file) computeBase(addr uint64) error {
	if f == nil || f.m == nil {
		return nil
	}
	if addr < f.m.start || addr >= f.m.limit {
		return fmt.Errorf("specified address %x is outside the mapping range [%x, %x] for file %q", addr, f.m.start, f.m.limit, f.name)
	}
	ef, err := elfOpen(f.name)
	if err != nil {
		return fmt.Errorf("error parsing %s: %v", f.name, err)
	}
	defer ef.Close()

	ph, err := f.m.findProgramHeader(ef, addr)
	if err != nil {
		return fmt.Errorf("failed to find program header for file %q, ELF mapping %#v, address %x: %v", f.name, *f.m, addr, err)
	}

	base, err := elfexec.GetBase(&ef.FileHeader, ph, f.m.kernelOffset, f.m.start, f.m.limit, f.m.offset)
	if err != nil {
		return err
	}
	f.base = base
	f.isData = ph != nil && ph.Flags&elf.PF_X == 0
	return nil
}

func (f *file) Name() string {
	return f.name
}

func (f *file) ObjAddr(addr uint64) (uint64, error) {
	f.baseOnce.Do(func() { f.baseErr = f.computeBase(addr) })
	if f.baseErr != nil {
		return 0, f.baseErr
	}
	return addr - f.base, nil
}

func (f *file) BuildID() string {
	return f.buildID
}

func (f *file) SourceLine(addr uint64) ([]plugin.Frame, error) {
	f.baseOnce.Do(func() { f.baseErr = f.computeBase(addr) })
	if f.baseErr != nil {
		return nil, f.baseErr
	}
	return nil, nil
}

func (f *file) Close() error {
	return nil
}

func (f *file) Symbols(r *regexp.Regexp, addr uint64) ([]*plugin.Sym, error) {
	// Get from nm a list of symbols sorted by address.
	cmd := exec.Command(f.b.nm, "-n", f.name)
	out, err := cmd.Output()
	if err != nil {
		return nil, fmt.Errorf("%v: %v", cmd.Args, err)
	}

	return findSymbols(out, f.name, r, addr)
}

// fileNM implements the binutils.ObjFile interface, using 'nm' to map
// addresses to symbols (without file/line number information). It is
// faster than fileAddr2Line.
type fileNM struct {
	file
	addr2linernm *addr2LinerNM
}

func (f *fileNM) SourceLine(addr uint64) ([]plugin.Frame, error) {
	f.baseOnce.Do(func() { f.baseErr = f.computeBase(addr) })
	if f.baseErr != nil {
		return nil, f.baseErr
	}
	if f.addr2linernm == nil {
		addr2liner, err := newAddr2LinerNM(f.b.nm, f.name, f.base)
		if err != nil {
			return nil, err
		}
		f.addr2linernm = addr2liner
	}
	return f.addr2linernm.addrInfo(addr)
}

// fileAddr2Line implements the binutils.ObjFile interface, using
// llvm-symbolizer, if that's available, or addr2line to map addresses to
// symbols (with file/line number information). It can be slow for large
// binaries with debug information.
type fileAddr2Line struct {
	once sync.Once
	file
	addr2liner     *addr2Liner
	llvmSymbolizer *llvmSymbolizer
	isData         bool
}

func (f *fileAddr2Line) SourceLine(addr uint64) ([]plugin.Frame, error) {
	f.baseOnce.Do(func() { f.baseErr = f.computeBase(addr) })
	if f.baseErr != nil {
		return nil, f.baseErr
	}
	f.once.Do(f.init)
	if f.llvmSymbolizer != nil {
		return f.llvmSymbolizer.addrInfo(addr)
	}
	if f.addr2liner != nil {
		return f.addr2liner.addrInfo(addr)
	}
	return nil, fmt.Errorf("could not find local addr2liner")
}

func (f *fileAddr2Line) init() {
	if llvmSymbolizer, err := newLLVMSymbolizer(f.b.llvmSymbolizer, f.name, f.base, f.isData); err == nil {
		f.llvmSymbolizer = llvmSymbolizer
		return
	}

	if addr2liner, err := newAddr2Liner(f.b.addr2line, f.name, f.base); err == nil {
		f.addr2liner = addr2liner

		// When addr2line encounters some gcc compiled binaries, it
		// drops interesting parts of names in anonymous namespaces.
		// Fallback to NM for better function names.
		if nm, err := newAddr2LinerNM(f.b.nm, f.name, f.base); err == nil {
			f.addr2liner.nm = nm
		}
	}
}

func (f *fileAddr2Line) Close() error {
	if f.llvmSymbolizer != nil {
		f.llvmSymbolizer.rw.close()
		f.llvmSymbolizer = nil
	}
	if f.addr2liner != nil {
		f.addr2liner.rw.close()
		f.addr2liner = nil
	}
	return nil
}


================================================
FILE: internal/binutils/binutils_test.go
================================================
// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package binutils

import (
	"bytes"
	"debug/elf"
	"encoding/binary"
	"errors"
	"fmt"
	"math"
	"path/filepath"
	"reflect"
	"regexp"
	"runtime"
	"slices"
	"strings"
	"testing"

	"github.com/google/pprof/internal/plugin"
)

var testAddrMap = map[int]string{
	1000: "_Z3fooid.clone2",
	2000: "_ZNSaIiEC1Ev.clone18",
	3000: "_ZNSt6vectorIS_IS_IiSaIiEESaIS1_EESaIS3_EEixEm",
}

func functionName(level int) (name string) {
	if name = testAddrMap[level]; name != "" {
		return name
	}
	return fmt.Sprintf("fun%d", level)
}

func TestAddr2Liner(t *testing.T) {
	const offset = 0x500

	a := addr2Liner{rw: &mockAddr2liner{}, base: offset}
	for i := 1; i < 8; i++ {
		addr := i*0x1000 + offset
		s, err := a.addrInfo(uint64(addr))
		if err != nil {
			t.Fatalf("addrInfo(%#x): %v", addr, err)
		}
		if len(s) != i {
			t.Fatalf("addrInfo(%#x): got len==%d, want %d", addr, len(s), i)
		}
		for l, f := range s {
			level := (len(s) - l) * 1000
			want := plugin.Frame{Func: functionName(level), File: fmt.Sprintf("file%d", level), Line: level}

			if f != want {
				t.Errorf("AddrInfo(%#x)[%d]: = %+v, want %+v", addr, l, f, want)
			}
		}
	}
	s, err := a.addrInfo(0xFFFF)
	if err != nil {
		t.Fatalf("addrInfo(0xFFFF): %v", err)
	}
	if len(s) != 0 {
		t.Fatalf("AddrInfo(0xFFFF): got len==%d, want 0", len(s))
	}
	a.rw.close()
}

type mockAddr2liner struct {
	output []string
}

func (a *mockAddr2liner) write(s string) error {
	var lines []string
	switch s {
	case "1000":
		lines = []string{"_Z3fooid.clone2", "file1000:1000"}
	case "2000":
		lines = []string{"_ZNSaIiEC1Ev.clone18", "file2000:2000", "_Z3fooid.clone2", "file1000:1000"}
	case "3000":
		lines = []string{"_ZNSt6vectorIS_IS_IiSaIiEESaIS1_EESaIS3_EEixEm", "file3000:3000", "_ZNSaIiEC1Ev.clone18", "file2000:2000", "_Z3fooid.clone2", "file1000:1000"}
	case "4000":
		lines = []string{"fun4000", "file4000:4000", "_ZNSt6vectorIS_IS_IiSaIiEESaIS1_EESaIS3_EEixEm", "file3000:3000", "_ZNSaIiEC1Ev.clone18", "file2000:2000", "_Z3fooid.clone2", "file1000:1000"}
	case "5000":
		lines = []string{"fun5000", "file5000:5000", "fun4000", "file4000:4000", "_ZNSt6vectorIS_IS_IiSaIiEESaIS1_EESaIS3_EEixEm", "file3000:3000", "_ZNSaIiEC1Ev.clone18", "file2000:2000", "_Z3fooid.clone2", "file1000:1000"}
	case "6000":
		lines = []string{"fun6000", "file6000:6000", "fun5000", "file5000:5000", "fun4000", "file4000:4000", "_ZNSt6vectorIS_IS_IiSaIiEESaIS1_EESaIS3_EEixEm", "file3000:3000", "_ZNSaIiEC1Ev.clone18", "file2000:2000", "_Z3fooid.clone2", "file1000:1000"}
	case "7000":
		lines = []string{"fun7000", "file7000:7000", "fun6000", "file6000:6000", "fun5000", "file5000:5000", "fun4000", "file4000:4000", "_ZNSt6vectorIS_IS_IiSaIiEESaIS1_EESaIS3_EEixEm", "file3000:3000", "_ZNSaIiEC1Ev.clone18", "file2000:2000", "_Z3fooid.clone2", "file1000:1000"}
	case "8000":
		lines = []string{"fun8000", "file8000:8000", "fun7000", "file7000:7000", "fun6000", "file6000:6000", "fun5000", "file5000:5000", "fun4000", "file4000:4000", "_ZNSt6vectorIS_IS_IiSaIiEESaIS1_EESaIS3_EEixEm", "file3000:3000", "_ZNSaIiEC1Ev.clone18", "file2000:2000", "_Z3fooid.clone2", "file1000:1000"}
	case "9000":
		lines = []string{"fun9000", "file9000:9000", "fun8000", "file8000:8000", "fun7000", "file7000:7000", "fun6000", "file6000:6000", "fun5000", "file5000:5000", "fun4000", "file4000:4000", "_ZNSt6vectorIS_IS_IiSaIiEESaIS1_EESaIS3_EEixEm", "file3000:3000", "_ZNSaIiEC1Ev.clone18", "file2000:2000", "_Z3fooid.clone2", "file1000:1000"}
	default:
		lines = []string{"??", "??:0"}
	}
	a.output = append(a.output, "0x"+s)
	a.output = append(a.output, lines...)
	return nil
}

func (a *mockAddr2liner) readLine() (string, error) {
	if len(a.output) == 0 {
		return "", fmt.Errorf("end of file")
	}
	next := a.output[0]
	a.output = a.output[1:]
	return next, nil
}

func (a *mockAddr2liner) close() {
}

func TestAddr2LinerLookup(t *testing.T) {
	for _, tc := range []struct {
		desc             string
		nmOutput         string
		wantSymbolized   map[uint64]string
		wantUnsymbolized []uint64
	}{
		{
			desc: "odd symbol count",
			nmOutput: `
0x1000 T 1000 100
0x2000 T 2000 120
0x3000 T 3000 130
`,
			wantSymbolized: map[uint64]string{
				0x1000: "0x1000",
				0x1001: "0x1000",
				0x1FFF: "0x1000",
				0x2000: "0x2000",
				0x2001: "0x2000",
				0x3000: "0x3000",
				0x312f: "0x3000",
			},
			wantUnsymbolized: []uint64{0x0fff, 0x3130},
		},
		{
			desc: "even symbol count",
			nmOutput: `
0x1000 T 1000 100
0x2000 T 2000 120
0x3000 T 3000 130
0x4000 T 4000 140
`,
			wantSymbolized: map[uint64]string{
				0x1000: "0x1000",
				0x1001: "0x1000",
				0x1FFF: "0x1000",
				0x2000: "0x2000",
				0x2fff: "0x2000",
				0x3000: "0x3000",
				0x3fff: "0x3000",
				0x4000: "0x4000",
				0x413f: "0x4000",
			},
			wantUnsymbolized: []uint64{0x0fff, 0x4140},
		},
		{
			desc: "different symbol types",
			nmOutput: `
absolute_0x100 a 100
absolute_0x200 A 200
text_0x1000 t 1000 100
bss_0x2000 b 2000 120
data_0x3000 d 3000 130
rodata_0x4000 r 4000 140
weak_0x5000 v 5000 150
text_0x6000 T 6000 160
bss_0x7000 B 7000 170
data_0x8000 D 8000 180
rodata_0x9000 R 9000 190
weak_0xa000 V a000 1a0
weak_0xb000 W b000 1b0
`,
			wantSymbolized: map[uint64]string{
				0x1000: "text_0x1000",
				0x1FFF: "text_0x1000",
				0x2000: "bss_0x2000",
				0x211f: "bss_0x2000",
				0x3000: "data_0x3000",
				0x312f: "data_0x3000",
				0x4000: "rodata_0x4000",
				0x413f: "rodata_0x4000",
				0x5000: "weak_0x5000",
				0x514f: "weak_0x5000",
				0x6000: "text_0x6000",
				0x6fff: "text_0x6000",
				0x7000: "bss_0x7000",
				0x716f: "bss_0x7000",
				0x8000: "data_0x8000",
				0x817f: "data_0x8000",
				0x9000: "rodata_0x9000",
				0x918f: "rodata_0x9000",
				0xa000: "weak_0xa000",
				0xa19f: "weak_0xa000",
				0xb000: "weak_0xb000",
				0xb1af: "weak_0xb000",
			},
			wantUnsymbolized: []uint64{0x100, 0x200, 0x0fff, 0x2120, 0x3130, 0x4140, 0x5150, 0x7170, 0x8180, 0x9190, 0xa1a0, 0xb1b0},
		},
	} {
		t.Run(tc.desc, func(t *testing.T) {
			a, err := parseAddr2LinerNM(0, bytes.NewBufferString(tc.nmOutput))
			if err != nil {
				t.Fatalf("nm parse error: %v", err)
			}
			for address, want := range tc.wantSymbolized {
				if got, _ := a.addrInfo(address); !checkAddress(got, address, want) {
					t.Errorf("%x: got %v, want %s", address, got, want)
				}
			}
			for _, unknown := range tc.wantUnsymbolized {
				if got, _ := a.addrInfo(unknown); got != nil {
					t.Errorf("%x: got %v, want nil", unknown, got)
				}
			}
		})
	}
}

func checkAddress(got []plugin.Frame, address uint64, want string) bool {
	if len(got) != 1 {
		return false
	}
	return got[0].Func == want
}

func TestSetTools(t *testing.T) {
	// Test that multiple calls work.
	bu := &Binutils{}
	bu.SetTools("")
	bu.SetTools("")
}

func TestSetFastSymbolization(t *testing.T) {
	// Test that multiple calls work.
	bu := &Binutils{}
	bu.SetFastSymbolization(true)
	bu.SetFastSymbolization(false)
}

func skipUnlessLinuxAmd64(t *testing.T) {
	if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
		t.Skip("This test only works on x86-64 Linux")
	}
}

func skipUnlessDarwinAmd64(t *testing.T) {
	if runtime.GOOS != "darwin" || runtime.GOARCH != "amd64" {
		t.Skip("This test only works on x86-64 macOS")
	}
}

func skipUnlessWindowsAmd64(t *testing.T) {
	if runtime.GOOS != "windows" || runtime.GOARCH != "amd64" {
		t.Skip("This test only works on x86-64 Windows")
	}
}

func testDisasm(t *testing.T, intelSyntax bool) {
	_, llvmObjdump, buObjdump := findObjdump([]string{""})
	if !llvmObjdump && !buObjdump {
		t.Skip("cannot disasm: no objdump tool available")
	}

	bu := &Binutils{}
	var testexe string
	switch runtime.GOOS {
	case "linux":
		testexe = "exe_linux_64"
	case "darwin":
		testexe = "exe_mac_64"
	case "windows":
		testexe = "exe_windows_64.exe"
	default:
		t.Skipf("unsupported OS %q", runtime.GOOS)
	}

	insts, err := bu.Disasm(filepath.Join("testdata", testexe), 0, math.MaxUint64, intelSyntax)
	if err != nil {
		t.Fatalf("Disasm: unexpected error %v", err)
	}
	mainCount := 0
	for _, x := range insts {
		// macOS symbols have a leading underscore.
		if x.Function == "main" || x.Function == "_main" {
			mainCount++
		}
	}
	if mainCount == 0 {
		t.Error("Disasm: found no main instructions")
	}
}

func TestDisasm(t *testing.T) {
	if (runtime.GOOS != "linux" && runtime.GOOS != "darwin" && runtime.GOOS != "windows") || runtime.GOARCH != "amd64" {
		t.Skip("This test only works on x86-64 Linux, macOS or Windows")
	}
	testDisasm(t, false)
}

func TestDisasmIntelSyntax(t *testing.T) {
	if (runtime.GOOS != "linux" && runtime.GOOS != "darwin" && runtime.GOOS != "windows") || runtime.GOARCH != "amd64" {
		t.Skip("This test only works on x86_64 Linux, macOS or Windows as it tests Intel asm syntax")
	}
	testDisasm(t, true)
}

func findSymbol(syms []*plugin.Sym, name string) *plugin.Sym {
	for _, s := range syms {
		if slices.Contains(s.Name, name) {
			return s
		}
	}
	return nil
}

func TestObjFile(t *testing.T) {
	// If this test fails, check the address for main function in testdata/exe_linux_64
	// using the command 'nm -n '. Update the hardcoded addresses below to match
	// the addresses from the output.
	skipUnlessLinuxAmd64(t)
	for _, tc := range []struct {
		desc                 string
		start, limit, offset uint64
		addr                 uint64
	}{
		{"fixed load address", 0x400000, 0x4006fc, 0, 0x40052d},
		// True user-mode ASLR binaries are ET_DYN rather than ET_EXEC so this case
		// is a bit artificial except that it approximates the
		// vmlinux-with-kernel-ASLR case where the binary *is* ET_EXEC.
		{"simulated ASLR address", 0x500000, 0x5006fc, 0, 0x50052d},
	} {
		t.Run(tc.desc, func(t *testing.T) {
			bu := &Binutils{}
			f, err := bu.Open(filepath.Join("testdata", "exe_linux_64"), tc.start, tc.limit, tc.offset, "")
			if err != nil {
				t.Fatalf("Open: unexpected error %v", err)
			}
			defer f.Close()
			syms, err := f.Symbols(regexp.MustCompile("main"), 0)
			if err != nil {
				t.Fatalf("Symbols: unexpected error %v", err)
			}

			m := findSymbol(syms, "main")
			if m == nil {
				t.Fatalf("Symbols: did not find main")
			}
			addr, err := f.ObjAddr(tc.addr)
			if err != nil {
				t.Fatalf("ObjAddr(%x) failed: %v", tc.addr, err)
			}
			if addr != m.Start {
				t.Errorf("ObjAddr(%x) got %x, want %x", tc.addr, addr, m.Start)
			}
			gotFrames, err := f.SourceLine(tc.addr)
			if err != nil {
				t.Fatalf("SourceLine: unexpected error %v", err)
			}
			wantFrames := []plugin.Frame{
				{Func: "main", File: "/tmp/hello.c", Line: 3, StartLine: 3},
			}
			if !reflect.DeepEqual(gotFrames, wantFrames) {
				t.Fatalf("SourceLine for main: got %v; want %v\n", gotFrames, wantFrames)
			}
		})
	}
}

func TestMachoFiles(t *testing.T) {
	// If this test fails, check the address for main function in testdata/exe_mac_64
	// and testdata/lib_mac_64 using addr2line or gaddr2line. Update the
	// hardcoded addresses below to match the addresses from the output.
	skipUnlessDarwinAmd64(t)

	// Load `file`, pretending it was mapped at `start`. Then get the symbol
	// table. Check that it contains the symbol `sym` and that the address
	// `addr` gives the `expected` stack trace.
	for _, tc := range []struct {
		desc                 string
		file                 string
		start, limit, offset uint64
		addr                 uint64
		sym                  string
		expected             []plugin.Frame
	}{
		{"normal mapping", "exe_mac_64", 0x100000000, math.MaxUint64, 0,
			0x100000f50, "_main",
			[]plugin.Frame{
				{Func: "main", File: "/tmp/hello.c", Line: 3, StartLine: 3},
			}},
		{"other mapping", "exe_mac_64", 0x200000000, math.MaxUint64, 0,
			0x200000f50, "_main",
			[]plugin.Frame{
				{Func: "main", File: "/tmp/hello.c", Line: 3, StartLine: 3},
			}},
		{"lib normal mapping", "lib_mac_64", 0, math.MaxUint64, 0,
			0xfa0, "_bar",
			[]plugin.Frame{
				{Func: "bar", File: "/tmp/lib.c", Line: 5, StartLine: 5},
			}},
	} {
		t.Run(tc.desc, func(t *testing.T) {
			bu := &Binutils{}
			f, err := bu.Open(filepath.Join("testdata", tc.file), tc.start, tc.limit, tc.offset, "")
			if err != nil {
				t.Fatalf("Open: unexpected error %v", err)
			}
			t.Logf("binutils: %v", bu)
			if runtime.GOOS == "darwin" && !bu.rep.addr2lineFound && !bu.rep.llvmSymbolizerFound {
				// On macOS, user needs to install gaddr2line or llvm-symbolizer with
				// Homebrew, skip the test when the environment doesn't have it
				// installed.
				t.Skip("couldn't find addr2line or gaddr2line")
			}
			defer f.Close()
			syms, err := f.Symbols(nil, 0)
			if err != nil {
				t.Fatalf("Symbols: unexpected error %v", err)
			}

			m := findSymbol(syms, tc.sym)
			if m == nil {
				t.Fatalf("Symbols: could not find symbol %v", tc.sym)
			}
			gotFrames, err := f.SourceLine(tc.addr)
			if err != nil {
				t.Fatalf("SourceLine: unexpected error %v", err)
			}
			if !reflect.DeepEqual(gotFrames, tc.expected) {
				t.Fatalf("SourceLine for main: got %v; want %v\n", gotFrames, tc.expected)
			}
		})
	}
}

func TestLLVMSymbolizer(t *testing.T) {
	if runtime.GOOS != "linux" {
		t.Skip("testtdata/llvm-symbolizer has only been tested on linux")
	}

	cmd := filepath.Join("testdata", "fake-llvm-symbolizer")
	for _, c := range []struct {
		addr   uint64
		isData bool
		frames []plugin.Frame
	}{
		{0x10, false, []plugin.Frame{
			{Func: "Inlined_0x10", File: "foo.h", Line: 0, Column: 0, StartLine: 0},
			{Func: "Func_0x10", File: "foo.c", Line: 2, Column: 1, StartLine: 2},
		}},
		{0x20, true, []plugin.Frame{
			{Func: "foo_0x20", File: "0x20 8"},
		}},
	} {
		desc := fmt.Sprintf("Code %x", c.addr)
		if c.isData {
			desc = fmt.Sprintf("Data %x", c.addr)
		}
		t.Run(desc, func(t *testing.T) {
			symbolizer, err := newLLVMSymbolizer(cmd, "foo", 0, c.isData)
			if err != nil {
				t.Fatalf("newLLVMSymbolizer: unexpected error %v", err)
			}
			defer symbolizer.rw.close()

			frames, err := symbolizer.addrInfo(c.addr)
			if err != nil {
				t.Fatalf("LLVM: unexpected error %v", err)
			}
			if !reflect.DeepEqual(frames, c.frames) {
				t.Errorf("LLVM: expect %v; got %v\n", c.frames, frames)
			}
		})
	}
}

func TestPEFile(t *testing.T) {
	// If this test fails, check the address for main function in testdata/exe_windows_64.exe
	// using the command 'nm -n '. Update the hardcoded addresses below to match
	// the addresses from the output.
	skipUnlessWindowsAmd64(t)
	for _, tc := range []struct {
		desc                 string
		start, limit, offset uint64
		addr                 uint64
	}{
		{"fake mapping", 0, math.MaxUint64, 0, 0x140001594},
		{"fixed load address", 0x140000000, 0x140002000, 0, 0x140001594},
		{"simulated ASLR address", 0x150000000, 0x150002000, 0, 0x150001594},
	} {
		t.Run(tc.desc, func(t *testing.T) {
			bu := &Binutils{}
			f, err := bu.Open(filepath.Join("testdata", "exe_windows_64.exe"), tc.start, tc.limit, tc.offset, "")
			if err != nil {
				t.Fatalf("Open: unexpected error %v", err)
			}
			defer f.Close()
			syms, err := f.Symbols(regexp.MustCompile("main"), 0)
			if err != nil {
				t.Fatalf("Symbols: unexpected error %v", err)
			}

			m := findSymbol(syms, "main")
			if m == nil {
				t.Fatalf("Symbols: did not find main")
			}
			addr, err := f.ObjAddr(tc.addr)
			if err != nil {
				t.Fatalf("ObjAddr(%x) failed: %v", tc.addr, err)
			}
			if addr != m.Start {
				t.Errorf("ObjAddr(%x) got %x, want %x", tc.addr, addr, m.Start)
			}
			gotFrames, err := f.SourceLine(tc.addr)
			if err != nil {
				t.Fatalf("SourceLine: unexpected error %v", err)
			}
			wantFrames := []plugin.Frame{
				{Func: "main", File: "hello.c", Line: 3, Column: 12, StartLine: 3},
			}
			if !reflect.DeepEqual(gotFrames, wantFrames) {
				t.Fatalf("SourceLine for main: got %v; want %v\n", gotFrames, wantFrames)
			}
		})
	}
}

func TestOpenMalformedELF(t *testing.T) {
	// Test that opening a malformed ELF file will report an error containing
	// the word "ELF".
	bu := &Binutils{}
	_, err := bu.Open(filepath.Join("testdata", "malformed_elf"), 0, 0, 0, "")
	if err == nil {
		t.Fatalf("Open: unexpected success")
	}

	if !strings.Contains(err.Error(), "ELF") {
		t.Errorf("Open: got %v, want error containing 'ELF'", err)
	}
}

func TestOpenMalformedMachO(t *testing.T) {
	// Test that opening a malformed Mach-O file will report an error containing
	// the word "Mach-O".
	bu := &Binutils{}
	_, err := bu.Open(filepath.Join("testdata", "malformed_macho"), 0, 0, 0, "")
	if err == nil {
		t.Fatalf("Open: unexpected success")
	}

	if !strings.Contains(err.Error(), "Mach-O") {
		t.Errorf("Open: got %v, want error containing 'Mach-O'", err)
	}
}

func TestObjdumpVersionChecks(t *testing.T) {
	// Test that the objdump version strings are parsed properly.
	type testcase struct {
		desc string
		os   string
		ver  string
		want bool
	}

	for _, tc := range []testcase{
		{
			desc: "Valid Apple LLVM version string with usable version",
			os:   "darwin",
			ver:  "Apple LLVM version 11.0.3 (clang-1103.0.32.62)\nOptimized build.",
			want: true,
		},
		{
			desc: "Valid Apple LLVM version string with unusable version",
			os:   "darwin",
			ver:  "Apple LLVM version 10.0.0 (clang-1000.11.45.5)\nOptimized build.",
			want: false,
		},
		{
			desc: "Invalid Apple LLVM version string with usable version",
			os:   "darwin",
			ver:  "Apple LLVM versions 11.0.3 (clang-1103.0.32.62)\nOptimized build.",
			want: false,
		},
		{
			desc: "Valid LLVM version string with usable version",
			os:   "linux",
			ver:  "LLVM (http://llvm.org/):\nLLVM version 9.0.1\n\nOptimized build.",
			want: true,
		},
		{
			desc: "Valid LLVM version string with unusable version",
			os:   "linux",
			ver:  "LLVM (http://llvm.org/):\nLLVM version 6.0.1\n\nOptimized build.",
			want: false,
		},
		{
			desc: "Invalid LLVM version string with usable version",
			os:   "linux",
			ver:  "LLVM (http://llvm.org/):\nLLVM versions 9.0.1\n\nOptimized build.",
			want: false,
		},
		{
			desc: "Valid LLVM objdump version string with trunk",
			os:   runtime.GOOS,
			ver:  "LLVM (http://llvm.org/):\nLLVM version custom-trunk 124ffeb592a00bfe\nOptimized build.",
			want: true,
		},
		{
			desc: "Invalid LLVM objdump version string with trunk",
			os:   runtime.GOOS,
			ver:  "LLVM (http://llvm.org/):\nLLVM version custom-trank 124ffeb592a00bfe\nOptimized build.",
			want: false,
		},
		{
			desc: "Invalid LLVM objdump version string with trunk",
			os:   runtime.GOOS,
			ver:  "LLVM (http://llvm.org/):\nllvm version custom-trunk 124ffeb592a00bfe\nOptimized build.",
			want: false,
		},
	} {
		if runtime.GOOS == tc.os {
			if got := isLLVMObjdump(tc.ver); got != tc.want {
				t.Errorf("%v: got %v, want %v", tc.desc, got, tc.want)
			}
		}
	}
	for _, tc := range []testcase{
		{
			desc: "Valid GNU objdump version string",
			ver:  "GNU objdump (GNU Binutils) 2.34\nCopyright (C) 2020 Free Software Foundation, Inc.",
			want: true,
		},
		{
			desc: "Invalid GNU objdump version string",
			ver:  "GNU nm (GNU Binutils) 2.34\nCopyright (C) 2020 Free Software Foundation, Inc.",
			want: false,
		},
	} {
		if got := isBuObjdump(tc.ver); got != tc.want {
			t.Errorf("%v: got %v, want %v", tc.desc, got, tc.want)
		}
	}
}

func TestComputeBase(t *testing.T) {
	realELFOpen := elfOpen
	defer func() {
		elfOpen = realELFOpen
	}()

	tinyExecFile := &elf.File{
		FileHeader: elf.FileHeader{Type: elf.ET_EXEC},
		Progs: []*elf.Prog{
			{ProgHeader: elf.ProgHeader{Type: elf.PT_PHDR, Flags: elf.PF_R | elf.PF_X, Off: 0x40, Vaddr: 0x400040, Paddr: 0x400040, Filesz: 0x1f8, Memsz: 0x1f8, Align: 8}},
			{ProgHeader: elf.ProgHeader{Type: elf.PT_INTERP, Flags: elf.PF_R, Off: 0x238, Vaddr: 0x400238, Paddr: 0x400238, Filesz: 0x1c, Memsz: 0x1c, Align: 1}},
			{ProgHeader: elf.ProgHeader{Type: elf.PT_LOAD, Flags: elf.PF_R | elf.PF_X, Off: 0, Vaddr: 0, Paddr: 0, Filesz: 0xc80, Memsz: 0xc80, Align: 0x200000}},
			{ProgHeader: elf.ProgHeader{Type: elf.PT_LOAD, Flags: elf.PF_R | elf.PF_W, Off: 0xc80, Vaddr: 0x200c80, Paddr: 0x200c80, Filesz: 0x1f0, Memsz: 0x1f0, Align: 0x200000}},
		},
	}
	tinyBadBSSExecFile := &elf.File{
		FileHeader: elf.FileHeader{Type: elf.ET_EXEC},
		Progs: []*elf.Prog{
			{ProgHeader: elf.ProgHeader{Type: elf.PT_PHDR, Flags: elf.PF_R | elf.PF_X, Off: 0x40, Vaddr: 0x400040, Paddr: 0x400040, Filesz: 0x1f8, Memsz: 0x1f8, Align: 8}},
			{ProgHeader: elf.ProgHeader{Type: elf.PT_INTERP, Flags: elf.PF_R, Off: 0x238, Vaddr: 0x400238, Paddr: 0x400238, Filesz: 0x1c, Memsz: 0x1c, Align: 1}},
			{ProgHeader: elf.ProgHeader{Type: elf.PT_LOAD, Flags: elf.PF_R | elf.PF_X, Off: 0, Vaddr: 0, Paddr: 0, Filesz: 0xc80, Memsz: 0xc80, Align: 0x200000}},
			{ProgHeader: elf.ProgHeader{Type: elf.PT_LOAD, Flags: elf.PF_R | elf.PF_W, Off: 0xc80, Vaddr: 0x200c80, Paddr: 0x200c80, Filesz: 0x100, Memsz: 0x1f0, Align: 0x200000}},
			{ProgHeader: elf.ProgHeader{Type: elf.PT_LOAD, Flags: elf.PF_R | elf.PF_W, Off: 0xd80, Vaddr: 0x400d80, Paddr: 0x400d80, Filesz: 0x90, Memsz: 0x90, Align: 0x200000}},
		},
	}

	for _, tc := range []struct {
		desc       string
		file       *elf.File
		openErr    error
		mapping    *elfMapping
		addr       uint64
		wantError  bool
		wantBase   uint64
		wantIsData bool
	}{
		{
			desc:       "no elf mapping, no error",
			mapping:    nil,
			addr:       0x1000,
			wantBase:   0,
			wantIsData: false,
		},
		{
			desc:      "address outside mapping bounds means error",
			file:      &elf.File{},
			mapping:   &elfMapping{start: 0x2000, limit: 0x5000, offset: 0x1000},
			addr:      0x1000,
			wantError: true,
		},
		{
			desc:      "elf.Open failing means error",
			file:      &elf.File{FileHeader: elf.FileHeader{Type: elf.ET_EXEC}},
			openErr:   errors.New("elf.Open failed"),
			mapping:   &elfMapping{start: 0x2000, limit: 0x5000, offset: 0x1000},
			addr:      0x4000,
			wantError: true,
		},
		{
			desc:       "no loadable segments, no error",
			file:       &elf.File{FileHeader: elf.FileHeader{Type: elf.ET_EXEC}},
			mapping:    &elfMapping{start: 0x2000, limit: 0x5000, offset: 0x1000},
			addr:       0x4000,
			wantBase:   0,
			wantIsData: false,
		},
		{
			desc:      "unsupported executable type, Get Base returns error",
			file:      &elf.File{FileHeader: elf.FileHeader{Type: elf.ET_NONE}},
			mapping:   &elfMapping{start: 0x2000, limit: 0x5000, offset: 0x1000},
			addr:      0x4000,
			wantError: true,
		},
		{
			desc:       "tiny file select executable segment by offset",
			file:       tinyExecFile,
			mapping:    &elfMapping{start: 0x5000000, limit: 0x5001000, offset: 0x0},
			addr:       0x5000c00,
			wantBase:   0x5000000,
			wantIsData: false,
		},
		{
			desc:       "tiny file select data segment by offset",
			file:       tinyExecFile,
			mapping:    &elfMapping{start: 0x5200000, limit: 0x5201000, offset: 0x0},
			addr:       0x5200c80,
			wantBase:   0x5000000,
			wantIsData: true,
		},
		{
			desc:      "tiny file offset outside any segment means error",
			file:      tinyExecFile,
			mapping:   &elfMapping{start: 0x5200000, limit: 0x5201000, offset: 0x0},
			addr:      0x5200e70,
			wantError: true,
		},
		{
			desc:       "tiny file with bad BSS segment selects data segment by offset in initialized section",
			file:       tinyBadBSSExecFile,
			mapping:    &elfMapping{start: 0x5200000, limit: 0x5201000, offset: 0x0},
			addr:       0x5200d79,
			wantBase:   0x5000000,
			wantIsData: true,
		},
		{
			desc:      "tiny file with bad BSS segment with offset in uninitialized section means error",
			file:      tinyBadBSSExecFile,
			mapping:   &elfMapping{start: 0x5200000, limit: 0x5201000, offset: 0x0},
			addr:      0x5200d80,
			wantError: true,
		},
	} {
		t.Run(tc.desc, func(t *testing.T) {
			elfOpen = func(_ string) (*elf.File, error) {
				return tc.file, tc.openErr
			}
			f := file{m: tc.mapping}
			err := f.computeBase(tc.addr)
			if (err != nil) != tc.wantError {
				t.Errorf("got error %v, want any error=%v", err, tc.wantError)
			}
			if err != nil {
				return
			}
			if f.base != tc.wantBase {
				t.Errorf("got base %x, want %x", f.base, tc.wantBase)
			}
			if f.isData != tc.wantIsData {
				t.Errorf("got isData %v, want %v", f.isData, tc.wantIsData)
			}
		})
	}
}

func TestELFObjAddr(t *testing.T) {
	// The exe_linux_64 has two loadable program headers:
	//  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
	//                 0x00000000000006fc 0x00000000000006fc  R E    0x200000
	//  LOAD           0x0000000000000e10 0x0000000000600e10 0x0000000000600e10
	//                 0x0000000000000230 0x0000000000000238  RW     0x200000
	name := filepath.Join("testdata", "exe_linux_64")

	for _, tc := range []struct {
		desc                 string
		start, limit, offset uint64
		wantOpenError        bool
		addr                 uint64
		wantObjAddr          uint64
		wantAddrError        bool
	}{
		{"exec mapping, good address", 0x5400000, 0x5401000, 0, false, 0x5400400, 0x400400, false},
		{"exec mapping, address outside segment", 0x5400000, 0x5401000, 0, false, 0x5400800, 0, true},
		{"short data mapping, good address", 0x5600e00, 0x5602000, 0xe00, false, 0x5600e10, 0x600e10, false},
		{"short data mapping, address outside segment", 0x5600e00, 0x5602000, 0xe00, false, 0x5600e00, 0x600e00, false},
		{"page aligned data mapping, good address", 0x5600000, 0x5602000, 0, false, 0x5601000, 0x601000, false},
		{"page aligned data mapping, address outside segment", 0x5600000, 0x5602000, 0, false, 0x5601048, 0, true},
		{"bad file offset, no matching segment", 0x5600000, 0x5602000, 0x2000, false, 0x5600e10, 0, true},
		{"large mapping size, match by sample offset", 0x5600000, 0x5603000, 0, false, 0x5600e10, 0x600e10, false},
	} {
		t.Run(tc.desc, func(t *testing.T) {
			b := binrep{}
			o, err := b.openELF(name, tc.start, tc.limit, tc.offset, "")
			if (err != nil) != tc.wantOpenError {
				t.Errorf("openELF got error %v, want any error=%v", err, tc.wantOpenError)
			}
			if err != nil {
				return
			}
			got, err := o.ObjAddr(tc.addr)
			if (err != nil) != tc.wantAddrError {
				t.Errorf("ObjAddr got error %v, want any error=%v", err, tc.wantAddrError)
			}
			if err != nil {
				return
			}
			if got != tc.wantObjAddr {
				t.Errorf("got ObjAddr %x; want %x\n", got, tc.wantObjAddr)
			}
		})
	}
}

type buf struct {
	data []byte
}

// write appends a null-terminated string and returns its starting index.
func (b *buf) write(s string) uint32 {
	res := uint32(len(b.data))
	b.data = append(b.data, s...)
	b.data = append(b.data, '\x00')
	return res
}

// fakeELFFile generates a minimal valid ELF file, with fake .head.text and
// .text sections, and their corresponding _text and _stext start symbols,
// mimicking a kernel vmlinux image.
func fakeELFFile(t *testing.T) *elf.File {
	var (
		sizeHeader64  = binary.Size(elf.Header64{})
		sizeProg64    = binary.Size(elf.Prog64{})
		sizeSection64 = binary.Size(elf.Section64{})
	)

	const (
		textAddr  = 0xffff000010080000
		stextAddr = 0xffff000010081000
	)

	// Generate magic to identify as an ELF file.
	var ident [16]uint8
	ident[0] = '\x7f'
	ident[1] = 'E'
	ident[2] = 'L'
	ident[3] = 'F'
	ident[elf.EI_CLASS] = uint8(elf.ELFCLASS64)
	ident[elf.EI_DATA] = uint8(elf.ELFDATA2LSB)
	ident[elf.EI_VERSION] = uint8(elf.EV_CURRENT)
	ident[elf.EI_OSABI] = uint8(elf.ELFOSABI_NONE)

	// A single program header, containing code and starting at the _text address.
	progs := []elf.Prog64{{
		Type: uint32(elf.PT_LOAD), Flags: uint32(elf.PF_R | elf.PF_X), Off: 0x10000, Vaddr: textAddr, Paddr: textAddr, Filesz: 0x1234567, Memsz: 0x1234567, Align: 0x10000}}

	symNames := buf{}
	syms := []elf.Sym64{
		{}, // first symbol empty by convention
		{Name: symNames.write("_text"), Info: 0, Other: 0, Shndx: 0, Value: textAddr, Size: 0},
		{Name: symNames.write("_stext"), Info: 0, Other: 0, Shndx: 0, Value: stextAddr, Size: 0},
	}

	const numSections = 5
	// We'll write `textSize` zero bytes as contents of the .head.text and .text sections.
	const textSize = 16
	// Offset of section contents in the byte stream -- after header, program headers, and section headers.
	sectionsStart := uint64(sizeHeader64 + len(progs)*sizeProg64 + numSections*sizeSection64)

	secNames := buf{}
	sections := [numSections]elf.Section64{
		{Name: secNames.write(".head.text"), Type: uint32(elf.SHT_PROGBITS), Flags: uint64(elf.SHF_ALLOC | elf.SHF_EXECINSTR), Addr: textAddr, Off: sectionsStart, Size: textSize, Link: 0, Info: 0, Addralign: 2048, Entsize: 0},
		{Name: secNames.write(".text"), Type: uint32(elf.SHT_PROGBITS), Flags: uint64(elf.SHF_ALLOC | elf.SHF_EXECINSTR), Addr: stextAddr, Off: sectionsStart + textSize, Size: textSize, Link: 0, Info: 0, Addralign: 2048, Entsize: 0},
		{Name: secNames.write(".symtab"), Type: uint32(elf.SHT_SYMTAB), Flags: 0, Addr: 0, Off: sectionsStart + 2*textSize, Size: uint64(len(syms) * elf.Sym64Size), Link: 3 /*index of .strtab*/, Info: 0, Addralign: 8, Entsize: elf.Sym64Size},
		{Name: secNames.write(".strtab"), Type: uint32(elf.SHT_STRTAB), Flags: 0, Addr: 0, Off: sectionsStart + 2*textSize + uint64(len(syms)*elf.Sym64Size), Size: uint64(len(symNames.data)), Link: 0, Info: 0, Addralign: 1, Entsize: 0},
		{Name: secNames.write(".shstrtab"), Type: uint32(elf.SHT_STRTAB), Flags: 0, Addr: 0, Off: sectionsStart + 2*textSize + uint64(len(syms)*elf.Sym64Size+len(symNames.data)), Size: uint64(len(secNames.data)), Link: 0, Info: 0, Addralign: 1, Entsize: 0},
	}

	hdr := elf.Header64{
		Ident:     ident,
		Type:      uint16(elf.ET_DYN),
		Machine:   uint16(elf.EM_AARCH64),
		Version:   uint32(elf.EV_CURRENT),
		Entry:     textAddr,
		Phoff:     uint64(sizeHeader64),
		Shoff:     uint64(sizeHeader64 + len(progs)*sizeProg64),
		Flags:     0,
		Ehsize:    uint16(sizeHeader64),
		Phentsize: uint16(sizeProg64),
		Phnum:     uint16(len(progs)),
		Shentsize: uint16(sizeSection64),
		Shnum:     uint16(len(sections)),
		Shstrndx:  4, // index of .shstrtab
	}

	// Serialize all headers and sections into a single binary stream.
	var data bytes.Buffer
	for i, b := range []interface{}{hdr, progs, sections, [textSize]byte{}, [textSize]byte{}, syms, symNames.data, secNames.data} {
		err := binary.Write(&data, binary.LittleEndian, b)
		if err != nil {
			t.Fatalf("Write(%v) got err %v, want nil", i, err)
		}
	}

	// ... and parse it as and ELF file.
	ef, err := elf.NewFile(bytes.NewReader(data.Bytes()))
	if err != nil {
		t.Fatalf("elf.NewFile got err %v, want nil", err)
	}
	return ef
}

func TestELFKernelOffset(t *testing.T) {
	realELFOpen := elfOpen
	defer func() {
		elfOpen = realELFOpen
	}()

	wantAddr := uint64(0xffff000010082000)
	elfOpen = func(_ string) (*elf.File, error) {
		return fakeELFFile(t), nil
	}

	for _, tc := range []struct {
		name             string
		relocationSymbol string
		start            uint64
	}{
		{"text", "_text", 0xffff000020080000},
		{"stext", "_stext", 0xffff000020081000},
	} {

		b := binrep{}
		o, err := b.openELF("vmlinux", tc.start, 0xffffffffffffffff, tc.start, tc.relocationSymbol)
		if err != nil {
			t.Errorf("%v: openELF got error %v, want nil", tc.name, err)
			continue
		}

		addr, err := o.ObjAddr(0xffff000020082000)
		if err != nil {
			t.Errorf("%v: ObjAddr got err %v, want nil", tc.name, err)
			continue
		}
		if addr != wantAddr {
			t.Errorf("%v: ObjAddr got %x, want %x", tc.name, addr, wantAddr)
		}

	}
}


================================================
FILE: internal/binutils/disasm.go
================================================
// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package binutils

import (
	"bytes"
	"io"
	"regexp"
	"strconv"
	"strings"

	"github.com/google/pprof/internal/plugin"
	"github.com/ianlancetaylor/demangle"
)

var (
	nmOutputRE                = regexp.MustCompile(`^\s*([[:xdigit:]]+)\s+(.)\s+(.*)`)
	objdumpAsmOutputRE        = regexp.MustCompile(`^\s*([[:xdigit:]]+):\s+(.*)`)
	objdumpOutputFileLine     = regexp.MustCompile(`^;?\s?(.*):([0-9]+)`)
	objdumpOutputFunction     = regexp.MustCompile(`^;?\s?(\S.*)\(\):`)
	objdumpOutputFunctionLLVM = regexp.MustCompile(`^([[:xdigit:]]+)?\s?(.*):`)
)

func findSymbols(syms []byte, file string, r *regexp.Regexp, address uint64) ([]*plugin.Sym, error) {
	// Collect all symbols from the nm output, grouping names mapped to
	// the same address into a single symbol.

	// The symbols to return.
	var symbols []*plugin.Sym

	// The current group of symbol names, and the address they are all at.
	names, start := []string{}, uint64(0)

	buf := bytes.NewBuffer(syms)

	for {
		symAddr, name, err := nextSymbol(buf)
		if err == io.EOF {
			// Done. If there was an unfinished group, append it.
			if len(names) != 0 {
				if match := matchSymbol(names, start, symAddr-1, r, address); match != nil {
					symbols = append(symbols, &plugin.Sym{Name: match, File: file, Start: start, End: symAddr - 1})
				}
			}

			// And return the symbols.
			return symbols, nil
		}

		if err != nil {
			// There was some kind of serious error reading nm's output.
			return nil, err
		}

		// If this symbol is at the same address as the current group, add it to the group.
		if symAddr == start {
			names = append(names, name)
			continue
		}

		// Otherwise append the current group to the list of symbols.
		if match := matchSymbol(names, start, symAddr-1, r, address); match != nil {
			symbols = append(symbols, &plugin.Sym{Name: match, File: file, Start: start, End: symAddr - 1})
		}

		// And start a new group.
		names, start = []string{name}, symAddr
	}
}

// matchSymbol checks if a symbol is to be selected by checking its
// name to the regexp and optionally its address. It returns the name(s)
// to be used for the matched symbol, or nil if no match
func matchSymbol(names []string, start, end uint64, r *regexp.Regexp, address uint64) []string {
	if address != 0 && address >= start && address <= end {
		return names
	}
	for _, name := range names {
		if r == nil || r.MatchString(name) {
			return []string{name}
		}

		// Match all possible demangled versions of the name.
		for _, o := range [][]demangle.Option{
			{demangle.NoClones},
			{demangle.NoParams, demangle.NoEnclosingParams},
			{demangle.NoParams, demangle.NoEnclosingParams, demangle.NoTemplateParams},
		} {
			if demangled, err := demangle.ToString(name, o...); err == nil && r.MatchString(demangled) {
				return []string{demangled}
			}
		}
	}
	return nil
}

// disassemble parses the output of the objdump command and returns
// the assembly instructions in a slice.
func disassemble(asm []byte) ([]plugin.Inst, error) {
	buf := bytes.NewBuffer(asm)
	function, file, line := "", "", 0
	var assembly []plugin.Inst
	for {
		input, err := buf.ReadString('\n')
		if err != nil {
			if err != io.EOF {
				return nil, err
			}
			if input == "" {
				break
			}
		}
		input = strings.TrimSpace(input)

		if fields := objdumpAsmOutputRE.FindStringSubmatch(input); len(fields) == 3 {
			if address, err := strconv.ParseUint(fields[1], 16, 64); err == nil {
				assembly = append(assembly,
					plugin.Inst{
						Addr:     address,
						Text:     fields[2],
						Function: function,
						File:     file,
						Line:     line,
					})
				continue
			}
		}
		if fields := objdumpOutputFileLine.FindStringSubmatch(input); len(fields) == 3 {
			if l, err := strconv.ParseUint(fields[2], 10, 32); err == nil {
				file, line = fields[1], int(l)
			}
			continue
		}
		if fields := objdumpOutputFunction.FindStringSubmatch(input); len(fields) == 2 {
			function = fields[1]
			continue
		} else {
			if fields := objdumpOutputFunctionLLVM.FindStringSubmatch(input); len(fields) == 3 {
				function = fields[2]
				continue
			}
		}
		// Reset on unrecognized lines.
		function, file, line = "", "", 0
	}

	return assembly, nil
}

// nextSymbol parses the nm output to find the next symbol listed.
// Skips over any output it cannot recognize.
func nextSymbol(buf *bytes.Buffer) (uint64, string, error) {
	for {
		line, err := buf.ReadString('\n')
		if err != nil {
			if err != io.EOF || line == "" {
				return 0, "", err
			}
		}
		line = strings.TrimSpace(line)

		if fields := nmOutputRE.FindStringSubmatch(line); len(fields) == 4 {
			if address, err := strconv.ParseUint(fields[1], 16, 64); err == nil {
				return address, fields[3], nil
			}
		}
	}
}


================================================
FILE: internal/binutils/disasm_test.go
================================================
// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package binutils

import (
	"fmt"
	"regexp"
	"testing"

	"github.com/google/pprof/internal/plugin"
)

// TestFindSymbols tests the FindSymbols routine using a hardcoded nm output.
func TestFindSymbols(t *testing.T) {
	type testcase struct {
		query, syms string
		want        []plugin.Sym
	}

	testsyms := `0000000000001000 t lineA001
0000000000001000 t lineA002
0000000000001000 t line1000
0000000000002000 t line200A
0000000000002000 t line2000
0000000000002000 t line200B
0000000000003000 t line3000
0000000000003000 t _ZNK4DumbclEPKc
0000000000003000 t lineB00C
0000000000003000 t line300D
0000000000004000 t _the_end
	`
	testcases := []testcase{
		{
			"line.*[AC]",
			testsyms,
			[]plugin.Sym{
				{Name: []string{"lineA001"}, File: "object.o", Start: 0x1000, End: 0x1FFF},
				{Name: []string{"line200A"}, File: "object.o", Start: 0x2000, End: 0x2FFF},
				{Name: []string{"lineB00C"}, File: "object.o", Start: 0x3000, End: 0x3FFF},
			},
		},
		{
			"Dumb::operator",
			testsyms,
			[]plugin.Sym{
				{Name: []string{"Dumb::operator()(char const*) const"}, File: "object.o", Start: 0x3000, End: 0x3FFF},
			},
		},
	}

	for _, tc := range testcases {
		syms, err := findSymbols([]byte(tc.syms), "object.o", regexp.MustCompile(tc.query), 0)
		if err != nil {
			t.Fatalf("%q: findSymbols: %v", tc.query, err)
		}
		if err := checkSymbol(syms, tc.want); err != nil {
			t.Errorf("%q: %v", tc.query, err)
		}
	}
}

func checkSymbol(got []*plugin.Sym, want []plugin.Sym) error {
	if len(got) != len(want) {
		return fmt.Errorf("unexpected number of symbols %d (want %d)", len(got), len(want))
	}

	for i, g := range got {
		w := want[i]
		if len(g.Name) != len(w.Name) {
			return fmt.Errorf("names, got %d, want %d", len(g.Name), len(w.Name))
		}
		for n := range g.Name {
			if g.Name[n] != w.Name[n] {
				return fmt.Errorf("name %d, got %q, want %q", n, g.Name[n], w.Name[n])
			}
		}
		if g.File != w.File {
			return fmt.Errorf("filename, got %q, want %q", g.File, w.File)
		}
		if g.Start != w.Start {
			return fmt.Errorf("start address, got %#x, want %#x", g.Start, w.Start)
		}
		if g.End != w.End {
			return fmt.Errorf("end address, got %#x, want %#x", g.End, w.End)
		}
	}
	return nil
}

// TestFunctionAssembly tests the FunctionAssembly routine by using a
// fake objdump script.
func TestFunctionAssembly(t *testing.T) {
	type testcase struct {
		s    plugin.Sym
		asm  string
		want []plugin.Inst
	}
	testcases := []testcase{
		{
			plugin.Sym{Name: []string{"symbol1"}, Start: 0x1000, End: 0x1FFF},
			"  1000: instruction one\n  1001: instruction two\n  1002: instruction three\n  1003: instruction four",
			[]plugin.Inst{
				{Addr: 0x1000, Text: "instruction one"},
				{Addr: 0x1001, Text: "instruction two"},
				{Addr: 0x1002, Text: "instruction three"},
				{Addr: 0x1003, Text: "instruction four"},
			},
		},
		{
			plugin.Sym{Name: []string{"symbol2"}, Start: 0x2000, End: 0x2FFF},
			"  2000: instruction one\n  2001: instruction two",
			[]plugin.Inst{
				{Addr: 0x2000, Text: "instruction one"},
				{Addr: 0x2001, Text: "instruction two"},
			},
		},
		{
			plugin.Sym{Name: []string{"_main"}, Start: 0x30000, End: 0x3FFF},
			"_main:\n; /tmp/hello.c:3\n30001:	push   %rbp",
			[]plugin.Inst{
				{Addr: 0x30001, Text: "push   %rbp", Function: "_main", File: "/tmp/hello.c", Line: 3},
			},
		},
		{
			plugin.Sym{Name: []string{"main"}, Start: 0x4000, End: 0x4FFF},
			"000000000040052d <main>:\nmain():\n/tmp/hello.c:3\n40001:	push   %rbp",
			[]plugin.Inst{
				{Addr: 0x40001, Text: "push   %rbp", Function: "main", File: "/tmp/hello.c", Line: 3},
			},
		},
	}

	for _, tc := range testcases {
		insts, err := disassemble([]byte(tc.asm))
		if err != nil {
			t.Fatalf("FunctionAssembly: %v", err)
		}

		if len(insts) != len(tc.want) {
			t.Errorf("Unexpected number of assembly instructions %d (want %d)\n", len(insts), len(tc.want))
		}
		for i := range insts {
			if insts[i] != tc.want[i] {
				t.Errorf("Expected symbol %v, got %v\n", tc.want[i], insts[i])
			}
		}
	}
}


================================================
FILE: internal/binutils/testdata/build_binaries.go
================================================
// Copyright 2019 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// This is a script that generates the test executables for MacOS and Linux
// in this directory. It should be needed very rarely to run this script.
// It is mostly provided as a future reference on how the original binary
// set was created.

// When a new executable is generated, hardcoded addresses in the
// functions TestObjFile, TestMachoFiles, TestPEFile in binutils_test.go must be updated.
package main

import (
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
)

func main() {
	wd, err := os.Getwd()
	if err != nil {
		log.Fatal(err)
	}

	switch runtime.GOOS {
	case "linux":
		if err := removeGlob("exe_linux_64*"); err != nil {
			log.Fatal(err)
		}

		out, err := exec.Command("cc", "-g", "-ffile-prefix-map="+wd+"="+"/tmp", "-o", "exe_linux_64", "hello.c").CombinedOutput()
		log.Println(string(out))
		if err != nil {
			log.Fatal(err)
		}

	case "darwin":
		if err := removeGlob("exe_mac_64*", "lib_mac_64"); err != nil {
			log.Fatal(err)
		}

		out, err := exec.Command("clang", "-g", "-ffile-prefix-map="+wd+"="+"/tmp", "-o", "exe_mac_64", "hello.c").CombinedOutput()
		log.Println(string(out))
		if err != nil {
			log.Fatal(err)
		}

		out, err = exec.Command("clang", "-g", "-ffile-prefix-map="+wd+"="+"/tmp", "-o", "lib_mac_64", "-dynamiclib", "lib.c").CombinedOutput()
		log.Println(string(out))
		if err != nil {
			log.Fatal(err)
		}

	case "windows":
		// Many gcc environments may create binaries that trigger false-positives
		// in antiviruses. MSYS2 with gcc 10.2.0 is a working environment for
		// compiling. To setup the environment follow the guide at
		// https://www.msys2.org/ and install gcc with `pacman -S gcc`.
		out, err := exec.Command("gcc", "-g", "-ffile-prefix-map="+wd+"=", "-o", "exe_windows_64.exe", "hello.c").CombinedOutput()
		log.Println(string(out))
		if err != nil {
			log.Fatal(err)
		}
		log.Println("Please verify that exe_windows_64.exe does not trigger any antivirus on `virustotal.com`.")
	default:
		log.Fatalf("Unsupported OS %q", runtime.GOOS)
	}
}

func removeGlob(globs ...string) error {
	for _, glob := range globs {
		matches, err := filepath.Glob(glob)
		if err != nil {
			return err
		}
		for _, p := range matches {
			os.Remove(p)
		}
	}
	return nil
}


================================================
FILE: internal/binutils/testdata/exe_mac_64.dSYM/Contents/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>CFBundleDevelopmentRegion</key>
    <string>English</string>
    <key>CFBundleIdentifier</key>
    <string>com.apple.xcode.dsym.exe_mac_64</string>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundlePackageType</key>
    <string>dSYM</string>
    <key>CFBundleSignature</key>
    <string>????</string>
    <key>CFBundleShortVersionString</key>
    <string>1.0</string>
    <key>CFBundleVersion</key>
    <string>1</string>
  </dict>
</plist>


================================================
FILE: internal/binutils/testdata/fake-llvm-symbolizer
================================================
#!/bin/sh
#
# Copyright 2014 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Fake llvm-symbolizer to use in tests

set -f
IFS=" "

while read line; do
  # line has form:
  #    filename 0xaddr
  # Emit dummy output that matches llvm-symbolizer JSON output format.
  set -- ${line}
  kind=$1
  fname=$2
  addr=$3
  case ${kind} in
  CODE)
    echo "{\"Address\":\"${addr}\",\"ModuleName\":\"${fname}\",\"Symbol\":[{\"Column\":0,\"FileName\":\"${fname}.h\",\"FunctionName\":\"Inlined_${addr}\",\"Line\":0,\"StartLine\":0},{\"Column\":1,\"FileName\":\"${fname}.c\",\"FunctionName\":\"Func_${addr}\",\"Line\":2,\"StartLine\":2}]}"
    ;;
  DATA)
    echo "{\"Address\":\"${addr}\",\"ModuleName\":\"${fname}\",\"Data\":{\"Name\":\"${fname}_${addr}\",\"Size\":\"0x8\",\"Start\":\"${addr}\"}}"
    ;;
  *) exit 1;;
  esac
done


================================================
FILE: internal/binutils/testdata/hello.c
================================================
#include <stdio.h>

int main() {
  printf("Hello, world!\n");
  return 0;
}


================================================
FILE: internal/binutils/testdata/lib.c
================================================
int foo() {
  return 1;
}

int bar() {
  return 2;
}


================================================
FILE: internal/binutils/testdata/lib_mac_64.dSYM/Contents/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>CFBundleDevelopmentRegion</key>
    <string>English</string>
    <key>CFBundleIdentifier</key>
    <string>com.apple.xcode.dsym.lib_mac_64</string>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundlePackageType</key>
    <string>dSYM</string>
    <key>CFBundleSignature</key>
    <string>????</string>
    <key>CFBundleShortVersionString</key>
    <string>1.0</string>
    <key>CFBundleVersion</key>
    <string>1</string>
  </dict>
</plist>


================================================
FILE: internal/binutils/testdata/malformed_elf
================================================
ELF

================================================
FILE: internal/binutils/testdata/malformed_macho
================================================


================================================
FILE: internal/driver/cli.go
================================================
// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package driver

import (
	"errors"
	"fmt"
	"os"

	"github.com/google/pprof/internal/binutils"
	"github.com/google/pprof/internal/plugin"
)

type source struct {
	Sources   []string
	ExecName  string
	BuildID   string
	Base      []string
	DiffBase  bool
	Normalize bool

	Seconds            int
	Timeout            int
	Symbolize          string
	HTTPHostport       string
	HTTPDisableBrowser bool
	Comment            string
	AllFrames          bool
}

// parseFlags parses the command lines through the specified flags package
// and returns the source of the profile and optionally the command
// for the kind of report to generate (nil for interactive use).
func parseFlags(o *plugin.Options) (*source, []string, error) {
	flag := o.Flagset
	// Comparisons.
	flagDiffBase := flag.StringList("diff_base", "", "Source of base profile for comparison")
	flagBase := flag.StringList("base", "", "Source of base profile for profile subtraction")
	// Source options.
	flagSymbolize := flag.String("symbolize", "", "Options for profile symbolization")
	flagBuildID := flag.String("buildid", "", "Override build id for first mapping")
	flagTimeout := flag.Int("timeout", -1, "Timeout in seconds for fetching a profile")
	flagAddComment := flag.String("add_comment", "", "Free-form annotation to add to the profile")
	flagAllFrames := flag.Bool("all_frames", false, "Ignore drop_frames and keep_frames regexps")
	// CPU profile options
	flagSeconds := flag.Int("seconds", -1, "Length of time for dynamic profiles")
	// Heap profile options
	flagInUseSpace := flag.Bool("inuse_space", false, "Display in-use memory size")
	flagInUseObjects := flag.Bool("inuse_objects", false, "Display in-use object counts")
	flagAllocSpace := flag.Bool("alloc_space", false, "Display allocated memory size")
	flagAllocObjects := flag.Bool("alloc_objects", false, "Display allocated object counts")
	// Contention profile options
	flagTotalDelay := flag.Bool("total_delay", false, "Display total delay at each region")
	flagContentions := flag.Bool("contentions", false, "Display number of delays at each region")
	flagMeanDelay := flag.Bool("mean_delay", false, "Display mean delay at each region")
	flagTools := flag.String("tools", os.Getenv("PPROF_TOOLS"), "Path for object tool pathnames")

	flagHTTP := flag.String("http", "", "Present interactive web UI at the specified http host:port")
	flagNoBrowser := flag.Bool("no_browser", false, "Skip opening a browser for the interactive web UI")

	// Flags that set configuration properties.
	cfg := currentConfig()
	configFlagSetter := installConfigFlags(flag, &cfg)

	flagCommands := make(map[string]*bool)
	flagParamCommands := make(map[string]*string)
	for name, cmd := range pprofCommands {
		if cmd.hasParam {
			flagParamCommands[name] = flag.String(name, "", "Generate a report in "+name+" format, matching regexp")
		} else {
			flagCommands[name] = flag.Bool(name, false, "Generate a report in "+name+" format")
		}
	}

	args := flag.Parse(func() {
		o.UI.Print(usageMsgHdr +
			usage(true) +
			usageMsgSrc +
			flag.ExtraUsage() +
			usageMsgVars)
	})
	if len(args) == 0 {
		return nil, nil, errors.New("no profile source specified")
	}

	var execName string
	// Recognize first argument as an executable or buildid override.
	if len(args) > 1 {
		arg0 := args[0]
		if file, err := o.Obj.Open(arg0, 0, ^uint64(0), 0, ""); err == nil {
			file.Close()
			execName = arg0
			args = args[1:]
		}
	}

	// Apply any specified flags to cfg.
	if err := configFlagSetter(); err != nil {
		return nil, nil, err
	}

	cmd, err := outputFormat(flagCommands, flagParamCommands)
	if err != nil {
		return nil, nil, err
	}
	if cmd != nil && *flagHTTP != "" {
		return nil, nil, errors.New("-http is not compatible with an output format on the command line")
	}

	if *flagNoBrowser && *flagHTTP == "" {
		return nil, nil, errors.New("-no_browser only makes sense with -http")
	}

	si := cfg.SampleIndex
	si = sampleIndex(flagTotalDelay, si, "delay", "-total_delay", o.UI)
	si = sampleIndex(flagMeanDelay, si, "delay", "-mean_delay", o.UI)
	si = sampleIndex(flagContentions, si, "contentions", "-contentions", o.UI)
	si = sampleIndex(flagInUseSpace, si, "inuse_space", "-inuse_space", o.UI)
	si = sampleIndex(flagInUseObjects, si, "inuse_objects", "-inuse_objects", o.UI)
	si = sampleIndex(flagAllocSpace, si, "alloc_space", "-alloc_space", o.UI)
	si = sampleIndex(flagAllocObjects, si, "alloc_objects", "-alloc_objects", o.UI)
	cfg.SampleIndex = si

	if *flagMeanDelay {
		cfg.Mean = true
	}

	source := &source{
		Sources:            args,
		ExecName:           execName,
		BuildID:            *flagBuildID,
		Seconds:            *flagSeconds,
		Timeout:            *flagTimeout,
		Symbolize:          *flagSymbolize,
		HTTPHostport:       *flagHTTP,
		HTTPDisableBrowser: *flagNoBrowser,
		Comment:            *flagAddComment,
		AllFrames:          *flagAllFrames,
	}

	if err := source.addBaseProfiles(*flagBase, *flagDiffBase); err != nil {
		return nil, nil, err
	}

	normalize := cfg.Normalize
	if normalize && len(source.Base) == 0 {
		return nil, nil, errors.New("must have base profile to normalize by")
	}
	source.Normalize = normalize

	if bu, ok := o.Obj.(*binutils.Binutils); ok {
		bu.SetTools(*flagTools)
	}

	setCurrentConfig(cfg)
	return source, cmd, nil
}

// addBaseProfiles adds the list of base profiles or diff base profiles to
// the source. This function will return an error if both base and diff base
// profiles are specified.
func (source *source) addBaseProfiles(flagBase, flagDiffBase []*string) error {
	base, diffBase := dropEmpty(flagBase), dropEmpty(flagDiffBase)
	if len(base) > 0 && len(diffBase) > 0 {
		return errors.New("-base and -diff_base flags cannot both be specified")
	}

	source.Base = base
	if len(diffBase) > 0 {
		source.Base, source.DiffBase = diffBase, true
	}
	return nil
}

// dropEmpty list takes a slice of string pointers, and outputs a slice of
// non-empty strings associated with the flag.
func dropEmpty(list []*string) []string {
	var l []string
	for _, s := range list {
		if *s != "" {
			l = append(l, *s)
		}
	}
	return l
}

// installConfigFlags creates command line flags for configuration
// fields and returns a function which can be called after flags have
// been parsed to copy any flags specified on the command line to
// *cfg.
func installConfigFlags(flag plugin.FlagSet, cfg *config) func() error {
	// List of functions for setting the different parts of a config.
	var setters []func()
	var err error // Holds any errors encountered while running setters.

	for _, field := range configFields {
		n := field.name
		help := configHelp[n]
		var setter func()
		switch ptr := cfg.fieldPtr(field).(type) {
		case *bool:
			f := flag.Bool(n, *ptr, help)
			setter = func() { *ptr = *f }
		case *int:
			f := flag.Int(n, *ptr, help)
			setter = func() { *ptr = *f }
		case *float64:
			f := flag.Float64(n, *ptr, help)
			setter = func() { *ptr = *f }
		case *string:
			if len(field.choices) == 0 {
				f := flag.String(n, *ptr, help)
				setter = func() { *ptr = *f }
			} else {
				// Make a separate flag per possible choice.
				// Set all flags to initially false so we can
				// identify conflicts.
				bools := make(map[string]*bool)
				for _, choice := range field.choices {
					bools[choice] = flag.Bool(choice, false, configHelp[choice])
				}
				setter = func() {
					var set []string
					for k, v := range bools {
						if *v {
							set = append(set, k)
						}
					}
					switch len(set) {
					case 0:
						// Leave as default value.
					case 1:
						*ptr = set[0]
					default:
						err = fmt.Errorf("conflicting options set: %v", set)
					}
				}
			}
		}
		setters = append(setters, setter)
	}

	return func() error {
		// Apply the setter for every flag.
		for _, setter := range setters {
			setter()
			if err != nil {
				return err
			}
		}
		return nil
	}
}

func sampleIndex(flag *bool, si string, sampleType, option string, ui plugin.UI) string {
	if *flag {
		if si == "" {
			return sampleType
		}
		ui.PrintErr("Multiple value selections, ignoring ", option)
	}
	return si
}

func outputFormat(bcmd map[string]*bool, acmd map[string]*string) (cmd []string, err error) {
	for n, b := range bcmd {
		if *b {
			if cmd != nil {
				return nil, errors.New("must set at most one output format")
			}
			cmd = []string{n}
		}
	}
	for n, s := range acmd {
		if *s != "" {
			if cmd != nil {
				return nil, errors.New("must set at most one output format")
			}
			cmd = []string{n, *s}
		}
	}
	return cmd, nil
}

var usageMsgHdr = `usage:

Produce output in the specified format.

   pprof <format> [options] [binary] <source> ...

Omit the format to get an interactive shell whose commands can be used
to generate various views of a profile

   pprof [options] [binary] <source> ...

Omit the format and provide the "-http" flag to get an interactive web
interface at the specified host:port that can be used to navigate through
various views of a profile.

   pprof -http [host]:[port] [options] [binary] <source> ...

Details:
`

var usageMsgSrc = "\n\n" +
	"  Source options:\n" +
	"    -seconds              Duration for time-based profile collection\n" +
	"    -timeout              Timeout in seconds for profile collection\n" +
	"    -buildid              Override build id for main binary\n" +
	"    -add_comment          Free-form annotation to add to the profile\n" +
	"                          Displayed on some reports or with pprof -comments\n" +
	"    -diff_base source     Source of base profile for comparison\n" +
	"    -base source          Source of base profile for profile subtraction\n" +
	"    profile.pb.gz         Profile in compressed protobuf format\n" +
	"    legacy_profile        Profile in legacy pprof format\n" +
	"    http://host/profile   URL for profile handler to retrieve\n" +
	"    -symbolize=           Controls source of symbol information\n" +
	"      none                  Do not attempt symbolization\n" +
	"      local                 Examine only local binaries\n" +
	"      fastlocal             Only get function names from local binaries\n" +
	"      remote                Do not examine local binaries\n" +
	"      force                 Force re-symbolization\n" +
	"    Binary                  Local path or build id of binary for symbolization\n"

var usageMsgVars = "\n\n" +
	"  Misc options:\n" +
	"   -http              Provide web interface at host:port.\n" +
	"                      Host is optional and 'localhost' by default.\n" +
	"                      Port is optional and a randomly available port by default.\n" +
	"   -no_browser        Skip opening a browser for the interactive web UI.\n" +
	"   -tools             Search path for object tools\n" +
	"   -all_frames        Ignore drop_frames and keep_frames regexps in the profile\n" +
	"                      Rarely needed, mainly used for debugging pprof itself\n" +
	"\n" +
	"  Legacy convenience options:\n" +
	"   -inuse_space           Same as -sample_index=inuse_space\n" +
	"   -inuse_objects         Same as -sample_index=inuse_objects\n" +
	"   -alloc_space           Same as -sample_index=alloc_space\n" +
	"   -alloc_objects         Same as -sample_index=alloc_objects\n" +
	"   -total_delay           Same as -sample_index=delay\n" +
	"   -contentions           Same as -sample_index=contentions\n" +
	"   -mean_delay            Same as -mean -sample_index=delay\n" +
	"\n" +
	"  Environment Variables:\n" +
	"   PPROF_TMPDIR       Location for saved profiles (default $HOME/pprof)\n" +
	"   PPROF_TOOLS        Search path for object-level tools\n" +
	"   PPROF_BINARY_PATH  Search path for local binary files\n" +
	"                      default: $HOME/pprof/binaries\n" +
	"                      searches $buildid/$name, $buildid/*, $path/$buildid,\n" +
	"                      ${buildid:0:2}/${buildid:2}.debug, $name, $path,\n" +
	"                      ${name}.debug, $dir/.debug/${name}.debug,\n" +
	"                      usr/lib/debug/$dir/${name}.debug\n" +
	"   * On Windows, %USERPROFILE% is used instead of $HOME"


================================================
FILE: internal/driver/commands.go
================================================
// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package driver

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"os/exec"
	"runtime"
	"sort"
	"strings"
	"time"

	"github.com/google/pprof/internal/plugin"
	"github.com/google/pprof/internal/report"
)

// commands describes the commands accepted by pprof.
type commands map[string]*command

// command describes the actions for a pprof command. Includes a
// function for command-line completion, the report format to use
// during report generation, any postprocessing functions, and whether
// the command expects a regexp parameter (typically a function name).
type command struct {
	format      int           // report format to generate
	postProcess PostProcessor // postprocessing to run on report
	visualizer  PostProcessor // display output using some callback
	hasParam    bool          // collect a parameter from the CLI
	description string        // single-line description text saying what the command does
	usage       string        // multi-line help text saying how the command is used
}

// help returns a help string for a command.
func (c *command) help(name string) string {
	message := c.description + "\n"
	if c.usage != "" {
		message += "  Usage:\n"
		lines := strings.Split(c.usage, "\n")
		for _, line := range lines {
			message += fmt.Sprintf("    %s\n", line)
		}
	}
	return message + "\n"
}

// AddCommand adds an additional command to the set of commands
// accepted by pprof. This enables extensions to add new commands for
// specialized visualization formats. If the command specified already
// exists, it is overwritten.
func AddCommand(cmd string, format int, post PostProcessor, desc, usage string) {
	pprofCommands[cmd] = &command{format, post, nil, false, desc, usage}
}

// SetVariableDefault sets the default value for a pprof
// variable. This enables extensions to set their own defaults.
func SetVariableDefault(variable, value string) {
	configure(variable, value)
}

// PostProcessor is a function that applies post-processing to the report output
type PostProcessor func(input io.Reader, output io.Writer, ui plugin.UI) error

// interactiveMode is true if pprof is running on interactive mode, reading
// commands from its shell.
var interactiveMode = false

// pprofCommands are the report generation commands recognized by pprof.
var pprofCommands = commands{
	// Commands that require no post-processing.
	"comments": {report.Comments, nil, nil, false, "Output all profile comments", ""},
	"disasm":   {report.Dis, nil, nil, true, "Output assembly listings annotated with samples", listHelp("disasm", true)},
	"dot":      {report.Dot, nil, nil, false, "Outputs a graph in DOT format", reportHelp("dot", false, true)},
	"list":     {report.List, nil, nil, true, "Output annotated source for functions matching regexp", listHelp("list", false)},
	"peek":     {report.Tree, nil, nil, true, "Output callers/callees of functions matching regexp", "peek func_regex\nDisplay callers and callees of functions matching func_regex."},
	"raw":      {report.Raw, nil, nil, false, "Outputs a text representation of the raw profile", ""},
	"tags":     {report.Tags, nil, nil, false, "Outputs all tags in the profile", "tags [tag_regex]* [-ignore_regex]* [>file]\nList tags with key:value matching tag_regex and exclude ignore_regex."},
	"text":     {report.Text, nil, nil, false, "Outputs top entries in text form", reportHelp("text", true, true)},
	"top":      {report.Text, nil, nil, false, "Outputs top entries in text form", reportHelp("top", true, true)},
	"traces":   {report.Traces, nil, nil, false, "Outputs all profile samples in text form", ""},
	"tree":     {report.Tree, nil, nil, false, "Outputs a text rendering of call graph", reportHelp("tree", true, true)},

	// Save binary formats to a file
	"callgrind": {report.Callgrind, nil, awayFromTTY("callgraph.out"), false, "Outputs a graph in callgrind format", reportHelp("callgrind", false, true)},
	"proto":     {report.Proto, nil, awayFromTTY("pb.gz"), false, "Outputs the profile in compressed protobuf format", ""},
	"topproto":  {report.TopProto, nil, awayFromTTY("pb.gz"), false, "Outputs top entries in compressed protobuf format", ""},

	// Generate report in DOT format and postprocess with dot
	"gif": {report.Dot, invokeDot("gif"), awayFromTTY("gif"), false, "Outputs a graph image in GIF format", reportHelp("gif", false, true)},
	"pdf": {report.Dot, invokeDot("pdf"), awayFromTTY("pdf"), false, "Outputs a graph in PDF format", reportHelp("pdf", false, true)},
	"png": {report.Dot, invokeDot("png"), awayFromTTY("png"), false, "Outputs a graph image in PNG format", reportHelp("png", false, true)},
	"ps":  {report.Dot, invokeDot("ps"), awayFromTTY("ps"), false, "Outputs a graph in PS format", reportHelp("ps", false, true)},

	// Save SVG output into a file
	"svg": {report.Dot, massageDotSVG(), awayFromTTY("svg"), false, "Outputs a graph in SVG format", reportHelp("svg", false, true)},

	// Visualize postprocessed dot output
	"eog":    {report.Dot, invokeDot("svg"), invokeVisualizer("svg", []string{"eog"}), false, "Visualize graph through eog", reportHelp("eog", false, false)},
	"evince": {report.Dot, invokeDot("pdf"), invokeVisualizer("pdf", []string{"evince"}), false, "Visualize graph through evince", reportHelp("evince", false, false)},
	"gv":     {report.Dot, invokeDot("ps"), inv
Download .txt
gitextract_qb_j6arz/

├── .gitattributes
├── .github/
│   ├── ISSUE_TEMPLATE.md
│   ├── dependabot.yml
│   └── workflows/
│       └── ci.yaml
├── .gitignore
├── AUTHORS
├── CONTRIBUTING.md
├── CONTRIBUTORS
├── LICENSE
├── README.md
├── browsertests/
│   ├── README.md
│   ├── browser_test.go
│   ├── go.mod
│   ├── go.sum
│   ├── testdata/
│   │   ├── testfixture.js
│   │   └── testflame.js
│   └── testutils.go
├── doc/
│   └── README.md
├── driver/
│   └── driver.go
├── fuzz/
│   ├── README.md
│   ├── corpus/
│   │   ├── cppbench.cpu.pb
│   │   ├── empty
│   │   ├── go.crc32.cpu.pb
│   │   ├── gobench.cpu.pb
│   │   └── java.cpu.pb
│   ├── fuzz_test.go
│   ├── main.go
│   └── testdata/
│       └── 7e3c92482f6f39fc502b822ded792c589849cca8
├── go.mod
├── go.sum
├── internal/
│   ├── binutils/
│   │   ├── addr2liner.go
│   │   ├── addr2liner_llvm.go
│   │   ├── addr2liner_nm.go
│   │   ├── binutils.go
│   │   ├── binutils_test.go
│   │   ├── disasm.go
│   │   ├── disasm_test.go
│   │   └── testdata/
│   │       ├── build_binaries.go
│   │       ├── exe_linux_64
│   │       ├── exe_mac_64
│   │       ├── exe_mac_64.dSYM/
│   │       │   └── Contents/
│   │       │       ├── Info.plist
│   │       │       └── Resources/
│   │       │           └── DWARF/
│   │       │               └── exe_mac_64
│   │       ├── fake-llvm-symbolizer
│   │       ├── hello.c
│   │       ├── lib.c
│   │       ├── lib_mac_64
│   │       ├── lib_mac_64.dSYM/
│   │       │   └── Contents/
│   │       │       ├── Info.plist
│   │       │       └── Resources/
│   │       │           └── DWARF/
│   │       │               └── lib_mac_64
│   │       ├── malformed_elf
│   │       └── malformed_macho
│   ├── driver/
│   │   ├── cli.go
│   │   ├── commands.go
│   │   ├── config.go
│   │   ├── driver.go
│   │   ├── driver_focus.go
│   │   ├── driver_test.go
│   │   ├── fetch.go
│   │   ├── fetch_test.go
│   │   ├── flags.go
│   │   ├── html/
│   │   │   ├── common.css
│   │   │   ├── common.js
│   │   │   ├── graph.css
│   │   │   ├── graph.html
│   │   │   ├── header.html
│   │   │   ├── plaintext.html
│   │   │   ├── source.html
│   │   │   ├── stacks.css
│   │   │   ├── stacks.html
│   │   │   ├── stacks.js
│   │   │   └── top.html
│   │   ├── interactive.go
│   │   ├── interactive_test.go
│   │   ├── options.go
│   │   ├── settings.go
│   │   ├── settings_test.go
│   │   ├── stacks.go
│   │   ├── svg.go
│   │   ├── tagroot.go
│   │   ├── tagroot_test.go
│   │   ├── tempfile.go
│   │   ├── tempfile_test.go
│   │   ├── testdata/
│   │   │   ├── cppbench.contention
│   │   │   ├── cppbench.cpu
│   │   │   ├── cppbench.cpu_no_samples_type
│   │   │   ├── cppbench.small.contention
│   │   │   ├── file1000.src
│   │   │   ├── file2000.src
│   │   │   ├── file3000.src
│   │   │   ├── go.crc32.cpu
│   │   │   ├── go.nomappings.crash
│   │   │   ├── pprof.contention.cum.files.dot
│   │   │   ├── pprof.contention.flat.addresses.dot.focus.ignore
│   │   │   ├── pprof.cpu.addresses.traces
│   │   │   ├── pprof.cpu.call_tree.callgrind
│   │   │   ├── pprof.cpu.callgrind
│   │   │   ├── pprof.cpu.comments
│   │   │   ├── pprof.cpu.cum.lines.text.focus.hide
│   │   │   ├── pprof.cpu.cum.lines.text.hide
│   │   │   ├── pprof.cpu.cum.lines.text.show
│   │   │   ├── pprof.cpu.cum.lines.topproto.hide
│   │   │   ├── pprof.cpu.cum.lines.tree.show_from
│   │   │   ├── pprof.cpu.flat.addresses.disasm
│   │   │   ├── pprof.cpu.flat.addresses.noinlines.text
│   │   │   ├── pprof.cpu.flat.addresses.weblist
│   │   │   ├── pprof.cpu.flat.filefunctions.noinlines.text
│   │   │   ├── pprof.cpu.flat.functions.call_tree.dot
│   │   │   ├── pprof.cpu.flat.functions.dot
│   │   │   ├── pprof.cpu.flat.functions.noinlines.text
│   │   │   ├── pprof.cpu.flat.functions.text
│   │   │   ├── pprof.cpu.lines.topproto
│   │   │   ├── pprof.cpu.peek
│   │   │   ├── pprof.cpu.tags
│   │   │   ├── pprof.cpu.tags.focus.ignore
│   │   │   ├── pprof.cpu.traces
│   │   │   ├── pprof.cpusmall.flat.addresses.tree
│   │   │   ├── pprof.heap.callgrind
│   │   │   ├── pprof.heap.comments
│   │   │   ├── pprof.heap.cum.lines.tree.focus
│   │   │   ├── pprof.heap.cum.relative_percentages.tree.focus
│   │   │   ├── pprof.heap.flat.files.seconds.text
│   │   │   ├── pprof.heap.flat.files.text
│   │   │   ├── pprof.heap.flat.files.text.focus
│   │   │   ├── pprof.heap.flat.inuse_objects.text
│   │   │   ├── pprof.heap.flat.inuse_objects.text.all_frames
│   │   │   ├── pprof.heap.flat.inuse_space.dot.focus
│   │   │   ├── pprof.heap.flat.inuse_space.dot.focus.ignore
│   │   │   ├── pprof.heap.flat.lines.dot.focus
│   │   │   ├── pprof.heap.tags
│   │   │   ├── pprof.heap.tags.unit
│   │   │   ├── pprof.heap_alloc.flat.alloc_objects.text
│   │   │   ├── pprof.heap_alloc.flat.alloc_space.dot
│   │   │   ├── pprof.heap_alloc.flat.alloc_space.dot.focus
│   │   │   ├── pprof.heap_alloc.flat.alloc_space.dot.hide
│   │   │   ├── pprof.heap_request.relative_percentages.tags.focus
│   │   │   ├── pprof.heap_request.tags.focus
│   │   │   ├── pprof.heap_sizetags.dot
│   │   │   ├── pprof.heap_tags.traces
│   │   │   ├── pprof.long_name_funcs.dot
│   │   │   ├── pprof.long_name_funcs.text
│   │   │   └── pprof.unknown.flat.functions.call_tree.text
│   │   ├── webhtml.go
│   │   ├── webui.go
│   │   └── webui_test.go
│   ├── elfexec/
│   │   ├── elfexec.go
│   │   └── elfexec_test.go
│   ├── graph/
│   │   ├── dotgraph.go
│   │   ├── dotgraph_test.go
│   │   ├── graph.go
│   │   ├── graph_test.go
│   │   └── testdata/
│   │       ├── compose1.dot
│   │       ├── compose2.dot
│   │       ├── compose3.dot
│   │       ├── compose4.dot
│   │       ├── compose5.dot
│   │       ├── compose6.dot
│   │       ├── compose7.dot
│   │       └── compose9.dot
│   ├── measurement/
│   │   ├── measurement.go
│   │   └── measurement_test.go
│   ├── plugin/
│   │   └── plugin.go
│   ├── proftest/
│   │   ├── BUILD
│   │   ├── proftest.go
│   │   └── testdata/
│   │       └── large.cpu
│   ├── report/
│   │   ├── package.go
│   │   ├── package_test.go
│   │   ├── report.go
│   │   ├── report_test.go
│   │   ├── shortnames.go
│   │   ├── shortnames_test.go
│   │   ├── source.go
│   │   ├── source_html.go
│   │   ├── source_test.go
│   │   ├── stacks.go
│   │   ├── stacks_test.go
│   │   ├── synth.go
│   │   ├── synth_test.go
│   │   └── testdata/
│   │       ├── README.md
│   │       ├── sample/
│   │       │   └── sample.go
│   │       ├── sample.cpu
│   │       ├── source.dot
│   │       ├── source.rpt
│   │       ├── source1
│   │       └── source2
│   ├── symbolizer/
│   │   ├── symbolizer.go
│   │   └── symbolizer_test.go
│   ├── symbolz/
│   │   ├── symbolz.go
│   │   └── symbolz_test.go
│   └── transport/
│       └── transport.go
├── pprof.go
├── profile/
│   ├── encode.go
│   ├── filter.go
│   ├── filter_test.go
│   ├── index.go
│   ├── index_test.go
│   ├── legacy_java_profile.go
│   ├── legacy_profile.go
│   ├── legacy_profile_test.go
│   ├── merge.go
│   ├── merge_test.go
│   ├── profile.go
│   ├── profile_test.go
│   ├── proto.go
│   ├── proto_test.go
│   ├── prune.go
│   ├── prune_test.go
│   └── testdata/
│       ├── cppbench.contention
│       ├── cppbench.contention.string
│       ├── cppbench.cpu
│       ├── cppbench.cpu.string
│       ├── cppbench.growth
│       ├── cppbench.growth.string
│       ├── cppbench.heap
│       ├── cppbench.heap.string
│       ├── cppbench.thread
│       ├── cppbench.thread.all
│       ├── cppbench.thread.all.string
│       ├── cppbench.thread.none
│       ├── cppbench.thread.none.string
│       ├── cppbench.thread.string
│       ├── go.crc32.cpu
│       ├── go.crc32.cpu.string
│       ├── go.godoc.thread
│       ├── go.godoc.thread.string
│       ├── gobench.cpu
│       ├── gobench.cpu.string
│       ├── gobench.heap
│       ├── gobench.heap.string
│       ├── java.contention
│       ├── java.contention.string
│       ├── java.cpu
│       ├── java.cpu.string
│       ├── java.heap
│       └── java.heap.string
├── proto/
│   ├── README.md
│   └── profile.proto
├── test.sh
└── third_party/
    └── svgpan/
        ├── LICENSE
        ├── svgpan.go
        └── svgpan.js
Download .txt
SYMBOL INDEX (1120 symbols across 89 files)

FILE: browsertests/browser_test.go
  function maybeSkipBrowserTest (line 32) | func maybeSkipBrowserTest(t *testing.T) {
  function TestTopTable (line 50) | func TestTopTable(t *testing.T) {
  function TestFlameGraph (line 74) | func TestFlameGraph(t *testing.T) {
  function TestSource (line 95) | func TestSource(t *testing.T) {
  function newContext (line 114) | func newContext(ctx context.Context, t *testing.T) context.Context {
  function matchRegexp (line 138) | func matchRegexp(t *testing.T, query, re string) chromedp.ActionFunc {
  function matchInOrder (line 160) | func matchInOrder(t *testing.T, query string, sequence ...string) chrome...
  function eval (line 183) | func eval(t *testing.T, js string) chromedp.ActionFunc {

FILE: browsertests/testdata/testfixture.js
  class TestFixture (line 4) | class TestFixture {
    method constructor (line 5) | constructor() {
    method run (line 10) | run(name, subtest) {
    method setContext (line 19) | setContext(name, ...args) {
    method log (line 23) | log(...args) {
    method err (line 27) | err(...args) {

FILE: browsertests/testdata/testflame.js
  function TestFlame (line 1) | function TestFlame() {

FILE: browsertests/testutils.go
  function makeTestServer (line 17) | func makeTestServer(t testing.TB, prof *profile.Profile) *httptest.Server {
  constant addrBase (line 62) | addrBase = 0x1000
  constant fakeSource (line 63) | fakeSource = "testdata/file1000.src"
  type fakeObj (line 65) | type fakeObj struct
    method Close (line 67) | func (f fakeObj) Close() error                        { return nil }
    method Name (line 68) | func (f fakeObj) Name() string                        { return "testbi...
    method ObjAddr (line 69) | func (f fakeObj) ObjAddr(addr uint64) (uint64, error) { return addr, n...
    method BuildID (line 70) | func (f fakeObj) BuildID() string                     { return "" }
    method SourceLine (line 71) | func (f fakeObj) SourceLine(addr uint64) ([]driver.Frame, error) {
    method Symbols (line 74) | func (f fakeObj) Symbols(r *regexp.Regexp, addr uint64) ([]*driver.Sym...
  type fakeObjTool (line 91) | type fakeObjTool struct
    method Open (line 93) | func (obj fakeObjTool) Open(file string, start, limit, offset uint64, ...
    method Disasm (line 97) | func (obj fakeObjTool) Disasm(file string, start, end uint64, intelSyn...
  function makeFakeProfile (line 105) | func makeFakeProfile() *profile.Profile {
  type testFlags (line 161) | type testFlags
    method Bool (line 163) | func (flags testFlags) Bool(name string, def bool, usage string) *bool {
    method Int (line 166) | func (flags testFlags) Int(name string, def int, usage string) *int {
    method Float64 (line 169) | func (flags testFlags) Float64(name string, def float64, usage string)...
    method String (line 172) | func (flags testFlags) String(name string, def string, usage string) *...
    method StringList (line 175) | func (flags testFlags) StringList(name string, def string, usage strin...
    method ExtraUsage (line 178) | func (flags testFlags) ExtraUsage() string          { return "" }
    method AddExtraUsage (line 179) | func (flags testFlags) AddExtraUsage(eu string)     {}
    method Parse (line 180) | func (flags testFlags) Parse(usage func()) []string { return []string{...
  function getFlag (line 184) | func getFlag[T any](flags testFlags, name string, def T) *T {
  type testUI (line 192) | type testUI struct
    method ReadLine (line 196) | func (ui testUI) ReadLine(_ string) (string, error)     { return "", i...
    method IsTerminal (line 197) | func (ui testUI) IsTerminal() bool                      { return false }
    method WantBrowser (line 198) | func (ui testUI) WantBrowser() bool                     { return false }
    method SetAutoComplete (line 199) | func (ui testUI) SetAutoComplete(_ func(string) string) {}
    method Print (line 200) | func (ui testUI) Print(args ...interface{})             {}
    method PrintErr (line 201) | func (ui testUI) PrintErr(args ...interface{}) {
  type testFetcher (line 207) | type testFetcher struct
    method Fetch (line 211) | func (f testFetcher) Fetch(source string, duration, timeout time.Durat...

FILE: driver/driver.go
  function PProf (line 33) | func PProf(o *Options) error {
  type HTTPServerArgs (line 66) | type HTTPServerArgs
  type Options (line 69) | type Options struct
    method internalOptions (line 37) | func (o *Options) internalOptions() *plugin.Options {
  type Writer (line 82) | type Writer interface
  type FlagSet (line 88) | type FlagSet interface
  type Fetcher (line 121) | type Fetcher interface
  type Symbolizer (line 126) | type Symbolizer interface
  type MappingSources (line 132) | type MappingSources
  type ObjTool (line 138) | type ObjTool interface
  type Inst (line 152) | type Inst struct
  type ObjFile (line 161) | type ObjFile interface
  type Frame (line 189) | type Frame struct
  type Sym (line 198) | type Sym struct
  type UI (line 206) | type UI interface
  type internalObjTool (line 236) | type internalObjTool struct
    method Open (line 240) | func (o *internalObjTool) Open(file string, start, limit, offset uint6...
    method Disasm (line 277) | func (o *internalObjTool) Disasm(file string, start, end uint64, intel...
  type internalObjFile (line 248) | type internalObjFile struct
    method SourceLine (line 252) | func (f *internalObjFile) SourceLine(frame uint64) ([]plugin.Frame, er...
    method Symbols (line 264) | func (f *internalObjFile) Symbols(r *regexp.Regexp, addr uint64) ([]*p...
  type internalSymbolizer (line 291) | type internalSymbolizer struct
    method Symbolize (line 295) | func (s *internalSymbolizer) Symbolize(mode string, srcs plugin.Mappin...

FILE: fuzz/fuzz_test.go
  function TestParseData (line 25) | func TestParseData(t *testing.T) {

FILE: fuzz/main.go
  function Fuzz (line 24) | func Fuzz(data []byte) int {

FILE: internal/binutils/addr2liner.go
  constant defaultAddr2line (line 30) | defaultAddr2line = "addr2line"
  constant sentinel (line 34) | sentinel = ^uint64(0)
  type addr2Liner (line 39) | type addr2Liner struct
    method readFrame (line 125) | func (d *addr2Liner) readFrame() (plugin.Frame, bool) {
    method rawAddrInfo (line 173) | func (d *addr2Liner) rawAddrInfo(addr uint64) ([]plugin.Frame, error) {
    method addrInfo (line 210) | func (d *addr2Liner) addrInfo(addr uint64) ([]plugin.Frame, error) {
  type lineReaderWriter (line 55) | type lineReaderWriter interface
  type addr2LinerJob (line 61) | type addr2LinerJob struct
    method write (line 67) | func (a *addr2LinerJob) write(s string) error {
    method readLine (line 72) | func (a *addr2LinerJob) readLine() (string, error) {
    method close (line 81) | func (a *addr2LinerJob) close() {
  function newAddr2Liner (line 90) | func newAddr2Liner(cmd, file string, base uint64) (*addr2Liner, error) {

FILE: internal/binutils/addr2liner_llvm.go
  constant defaultLLVMSymbolizer (line 31) | defaultLLVMSymbolizer = "llvm-symbolizer"
  type llvmSymbolizer (line 36) | type llvmSymbolizer struct
    method readDataFrames (line 115) | func (d *llvmSymbolizer) readDataFrames() ([]plugin.Frame, error) {
    method readCodeFrames (line 145) | func (d *llvmSymbolizer) readCodeFrames() ([]plugin.Frame, error) {
    method addrInfo (line 173) | func (d *llvmSymbolizer) addrInfo(addr uint64) ([]plugin.Frame, error) {
  type llvmSymbolizerJob (line 44) | type llvmSymbolizerJob struct
    method write (line 52) | func (a *llvmSymbolizerJob) write(s string) error {
    method readLine (line 57) | func (a *llvmSymbolizerJob) readLine() (string, error) {
    method close (line 66) | func (a *llvmSymbolizerJob) close() {
  function newLLVMSymbolizer (line 75) | func newLLVMSymbolizer(cmd, file string, base uint64, isData bool) (*llv...

FILE: internal/binutils/addr2liner_nm.go
  constant defaultNM (line 29) | defaultNM = "nm"
  type addr2LinerNM (line 34) | type addr2LinerNM struct
    method addrInfo (line 117) | func (a *addr2LinerNM) addrInfo(addr uint64) ([]plugin.Frame, error) {
  type symbolInfo (line 38) | type symbolInfo struct
    method isData (line 46) | func (s *symbolInfo) isData() bool {
  function newAddr2LinerNM (line 62) | func newAddr2LinerNM(cmd, file string, base uint64) (*addr2LinerNM, erro...
  function parseAddr2LinerNM (line 75) | func parseAddr2LinerNM(base uint64, nm io.Reader) (*addr2LinerNM, error) {

FILE: internal/binutils/binutils.go
  type Binutils (line 40) | type Binutils struct
    method get (line 72) | func (bu *Binutils) get() *binrep {
    method update (line 85) | func (bu *Binutils) update(fn func(r *binrep)) {
    method String (line 99) | func (bu *Binutils) String() string {
    method SetFastSymbolization (line 121) | func (bu *Binutils) SetFastSymbolization(fast bool) {
    method SetTools (line 130) | func (bu *Binutils) SetTools(config string) {
    method Disasm (line 259) | func (bu *Binutils) Disasm(file string, start, end uint64, intelSyntax...
    method Open (line 287) | func (bu *Binutils) Open(name string, start, limit, offset uint64, rel...
  type binrep (line 54) | type binrep struct
    method openMachOCommon (line 358) | func (b *binrep) openMachOCommon(name string, of *macho.File, start, l...
    method openFatMachO (line 381) | func (b *binrep) openFatMachO(name string, start, limit, offset uint64...
    method openMachO (line 418) | func (b *binrep) openMachO(name string, start, limit, offset uint64) (...
    method openELF (line 428) | func (b *binrep) openELF(name string, start, limit, offset uint64, rel...
    method openPE (line 498) | func (b *binrep) openPE(name string, start, limit, offset uint64) (plu...
  function initTools (line 134) | func initTools(b *binrep, config string) {
  function findObjdump (line 163) | func findObjdump(paths []string) (string, bool, bool) {
  function chooseExe (line 194) | func chooseExe(names, osxNames []string, paths []string) (string, bool) {
  function isLLVMObjdump (line 209) | func isLLVMObjdump(output string) bool {
  function isBuObjdump (line 241) | func isBuObjdump(output string) bool {
  function findExe (line 247) | func findExe(cmd string, paths []string) (string, bool) {
  type elfMapping (line 527) | type elfMapping struct
    method findProgramHeader (line 537) | func (m *elfMapping) findProgramHeader(ef *elf.File, addr uint64) (*el...
  type file (line 576) | type file struct
    method computeBase (line 592) | func (f *file) computeBase(addr uint64) error {
    method Name (line 619) | func (f *file) Name() string {
    method ObjAddr (line 623) | func (f *file) ObjAddr(addr uint64) (uint64, error) {
    method BuildID (line 631) | func (f *file) BuildID() string {
    method SourceLine (line 635) | func (f *file) SourceLine(addr uint64) ([]plugin.Frame, error) {
    method Close (line 643) | func (f *file) Close() error {
    method Symbols (line 647) | func (f *file) Symbols(r *regexp.Regexp, addr uint64) ([]*plugin.Sym, ...
  type fileNM (line 661) | type fileNM struct
    method SourceLine (line 666) | func (f *fileNM) SourceLine(addr uint64) ([]plugin.Frame, error) {
  type fileAddr2Line (line 685) | type fileAddr2Line struct
    method SourceLine (line 693) | func (f *fileAddr2Line) SourceLine(addr uint64) ([]plugin.Frame, error) {
    method init (line 708) | func (f *fileAddr2Line) init() {
    method Close (line 726) | func (f *fileAddr2Line) Close() error {

FILE: internal/binutils/binutils_test.go
  function functionName (line 41) | func functionName(level int) (name string) {
  function TestAddr2Liner (line 48) | func TestAddr2Liner(t *testing.T) {
  type mockAddr2liner (line 80) | type mockAddr2liner struct
    method write (line 84) | func (a *mockAddr2liner) write(s string) error {
    method readLine (line 113) | func (a *mockAddr2liner) readLine() (string, error) {
    method close (line 122) | func (a *mockAddr2liner) close() {
  function TestAddr2LinerLookup (line 125) | func TestAddr2LinerLookup(t *testing.T) {
  function checkAddress (line 234) | func checkAddress(got []plugin.Frame, address uint64, want string) bool {
  function TestSetTools (line 241) | func TestSetTools(t *testing.T) {
  function TestSetFastSymbolization (line 248) | func TestSetFastSymbolization(t *testing.T) {
  function skipUnlessLinuxAmd64 (line 255) | func skipUnlessLinuxAmd64(t *testing.T) {
  function skipUnlessDarwinAmd64 (line 261) | func skipUnlessDarwinAmd64(t *testing.T) {
  function skipUnlessWindowsAmd64 (line 267) | func skipUnlessWindowsAmd64(t *testing.T) {
  function testDisasm (line 273) | func testDisasm(t *testing.T, intelSyntax bool) {
  function TestDisasm (line 308) | func TestDisasm(t *testing.T) {
  function TestDisasmIntelSyntax (line 315) | func TestDisasmIntelSyntax(t *testing.T) {
  function findSymbol (line 322) | func findSymbol(syms []*plugin.Sym, name string) *plugin.Sym {
  function TestObjFile (line 331) | func TestObjFile(t *testing.T) {
  function TestMachoFiles (line 384) | func TestMachoFiles(t *testing.T) {
  function TestLLVMSymbolizer (line 451) | func TestLLVMSymbolizer(t *testing.T) {
  function TestPEFile (line 492) | func TestPEFile(t *testing.T) {
  function TestOpenMalformedELF (line 543) | func TestOpenMalformedELF(t *testing.T) {
  function TestOpenMalformedMachO (line 557) | func TestOpenMalformedMachO(t *testing.T) {
  function TestObjdumpVersionChecks (line 571) | func TestObjdumpVersionChecks(t *testing.T) {
  function TestComputeBase (line 660) | func TestComputeBase(t *testing.T) {
  function TestELFObjAddr (line 794) | func TestELFObjAddr(t *testing.T) {
  type buf (line 842) | type buf struct
    method write (line 847) | func (b *buf) write(s string) uint32 {
  function fakeELFFile (line 857) | func fakeELFFile(t *testing.T) *elf.File {
  function TestELFKernelOffset (line 940) | func TestELFKernelOffset(t *testing.T) {

FILE: internal/binutils/disasm.go
  function findSymbols (line 36) | func findSymbols(syms []byte, file string, r *regexp.Regexp, address uin...
  function matchSymbol (line 86) | func matchSymbol(names []string, start, end uint64, r *regexp.Regexp, ad...
  function disassemble (line 111) | func disassemble(asm []byte) ([]plugin.Inst, error) {
  function nextSymbol (line 164) | func nextSymbol(buf *bytes.Buffer) (uint64, string, error) {

FILE: internal/binutils/disasm_test.go
  function TestFindSymbols (line 26) | func TestFindSymbols(t *testing.T) {
  function checkSymbol (line 74) | func checkSymbol(got []*plugin.Sym, want []plugin.Sym) error {
  function TestFunctionAssembly (line 104) | func TestFunctionAssembly(t *testing.T) {

FILE: internal/binutils/testdata/build_binaries.go
  function main (line 32) | func main() {
  function removeGlob (line 83) | func removeGlob(globs ...string) error {

FILE: internal/binutils/testdata/hello.c
  function main (line 3) | int main() {

FILE: internal/binutils/testdata/lib.c
  function foo (line 1) | int foo() {
  function bar (line 5) | int bar() {

FILE: internal/driver/cli.go
  type source (line 26) | type source struct
    method addBaseProfiles (line 174) | func (source *source) addBaseProfiles(flagBase, flagDiffBase []*string...
  function parseFlags (line 46) | func parseFlags(o *plugin.Options) (*source, []string, error) {
  function dropEmpty (line 189) | func dropEmpty(list []*string) []string {
  function installConfigFlags (line 203) | func installConfigFlags(flag plugin.FlagSet, cfg *config) func() error {
  function sampleIndex (line 267) | func sampleIndex(flag *bool, si string, sampleType, option string, ui pl...
  function outputFormat (line 277) | func outputFormat(bcmd map[string]*bool, acmd map[string]*string) (cmd [...

FILE: internal/driver/commands.go
  type commands (line 33) | type commands
  type command (line 39) | type command struct
    method help (line 49) | func (c *command) help(name string) string {
  function AddCommand (line 65) | func AddCommand(cmd string, format int, post PostProcessor, desc, usage ...
  function SetVariableDefault (line 71) | func SetVariableDefault(variable, value string) {
  type PostProcessor (line 76) | type PostProcessor
  function helpText (line 254) | func helpText(s ...string) string {
  function usage (line 260) | func usage(commandLine bool) string {
  function reportHelp (line 310) | func reportHelp(c string, cum, redirect bool) string {
  function listHelp (line 327) | func listHelp(c string, redirect bool) string {
  function browsers (line 341) | func browsers() []string {
  function awayFromTTY (line 370) | func awayFromTTY(format string) PostProcessor {
  function invokeDot (line 385) | func invokeDot(format string) PostProcessor {
  function massageDotSVG (line 398) | func massageDotSVG() PostProcessor {
  function invokeVisualizer (line 410) | func invokeVisualizer(suffix string, visualizers []string) PostProcessor {
  function stringToBool (line 452) | func stringToBool(s string) (bool, error) {

FILE: internal/driver/config.go
  type config (line 16) | type config struct
    method fieldPtr (line 200) | func (cfg *config) fieldPtr(f configField) interface{} {
    method get (line 210) | func (cfg *config) get(f configField) string {
    method set (line 225) | func (cfg *config) set(f configField, value string) error {
    method resetTransient (line 316) | func (cfg *config) resetTransient() {
    method applyURL (line 326) | func (cfg *config) applyURL(params url.Values) error {
    method makeURL (line 344) | func (cfg *config) makeURL(initialURL url.URL) (url.URL, bool) {
  function defaultConfig (line 63) | func defaultConfig() config {
  function currentConfig (line 81) | func currentConfig() config {
  function setCurrentConfig (line 87) | func setCurrentConfig(cfg config) {
  type configField (line 94) | type configField struct
  function init (line 111) | func init() {
  function isConfigurable (line 263) | func isConfigurable(name string) bool {
  function isBoolConfig (line 270) | func isBoolConfig(name string) bool {
  function completeConfig (line 284) | func completeConfig(prefix string) []string {
  function configure (line 296) | func configure(name, value string) error {

FILE: internal/driver/driver.go
  function PProf (line 37) | func PProf(eo *plugin.Options) error {
  function generateRawReport (line 64) | func generateRawReport(p *profile.Profile, cmd []string, cfg config, o *...
  function generateReport (line 114) | func generateReport(p *profile.Profile, cmd []string, cfg config, o *plu...
  function printWebList (line 166) | func printWebList(dst io.Writer, rpt *report.Report, obj plugin.ObjTool)...
  function applyCommandOverrides (line 178) | func applyCommandOverrides(cmd string, outputFormat int, cfg config) con...
  function generateTagRootsLeaves (line 235) | func generateTagRootsLeaves(prof *profile.Profile, cfg config, ui plugin...
  function dropEmptyStrings (line 244) | func dropEmptyStrings(in []string) (out []string) {
  function aggregate (line 253) | func aggregate(prof *profile.Profile, cfg config) error {
  function reportOptions (line 284) | func reportOptions(p *profile.Profile, numLabelUnits map[string]string, ...
  function identifyNumLabelUnits (line 353) | func identifyNumLabelUnits(p *profile.Profile, ui plugin.UI) map[string]...
  type sampleValueFunc (line 364) | type sampleValueFunc
  function sampleFormat (line 368) | func sampleFormat(p *profile.Profile, sampleIndex string, mean bool) (va...
  function valueExtractor (line 384) | func valueExtractor(ix int) sampleValueFunc {
  type profileCopier (line 392) | type profileCopier
    method newCopy (line 402) | func (c profileCopier) newCopy() *profile.Profile {
  function makeProfileCopier (line 394) | func makeProfileCopier(src *profile.Profile) profileCopier {

FILE: internal/driver/driver_focus.go
  function applyFocus (line 32) | func applyFocus(prof *profile.Profile, numLabelUnits map[string]string, ...
  function compileRegexOption (line 70) | func compileRegexOption(name, value string, err error) (*regexp.Regexp, ...
  function compileTagFilter (line 81) | func compileTagFilter(name, value string, numLabelUnits map[string]strin...
  function parseTagFilterRange (line 168) | func parseTagFilterRange(filter string) func(int64, string) bool {
  function warnNoMatches (line 214) | func warnNoMatches(match bool, option string, ui plugin.UI) {

FILE: internal/driver/driver_test.go
  function TestParse (line 40) | func TestParse(t *testing.T) {
  function removeScripts (line 230) | func removeScripts(in []byte) []byte {
  function addFlags (line 243) | func addFlags(f *testFlags, flags []string) {
  function testSourceURL (line 259) | func testSourceURL(port int) string {
  function solutionFilename (line 264) | func solutionFilename(source string, f *testFlags) string {
  function addString (line 291) | func addString(name []string, f *testFlags, components []string) []string {
  type testFlags (line 301) | type testFlags struct
    method ExtraUsage (line 310) | func (testFlags) ExtraUsage() string { return "" }
    method AddExtraUsage (line 312) | func (testFlags) AddExtraUsage(eu string) {}
    method Bool (line 314) | func (f testFlags) Bool(s string, d bool, c string) *bool {
    method Int (line 321) | func (f testFlags) Int(s string, d int, c string) *int {
    method Float64 (line 328) | func (f testFlags) Float64(s string, d float64, c string) *float64 {
    method String (line 335) | func (f testFlags) String(s, d, c string) *string {
    method StringList (line 342) | func (f testFlags) StringList(s, d, c string) *[]*string {
    method Parse (line 354) | func (f testFlags) Parse(func()) []string {
  function baseFlags (line 358) | func baseFlags() testFlags {
  constant testStart (line 379) | testStart = 0x1000
  constant testOffset (line 380) | testOffset = 0x5000
  type testFetcher (line 382) | type testFetcher struct
    method Fetch (line 384) | func (testFetcher) Fetch(s string, d, t time.Duration) (*profile.Profi...
  type testSymbolizer (line 433) | type testSymbolizer struct
    method Symbolize (line 435) | func (testSymbolizer) Symbolize(_ string, _ plugin.MappingSources, _ *...
  type testSymbolizeDemangler (line 439) | type testSymbolizeDemangler struct
    method Symbolize (line 441) | func (testSymbolizeDemangler) Symbolize(_ string, _ plugin.MappingSour...
  function testFetchSymbols (line 450) | func testFetchSymbols(source, post string) ([]byte, error) {
  type testSymbolzSymbolizer (line 481) | type testSymbolzSymbolizer struct
    method Symbolize (line 483) | func (testSymbolzSymbolizer) Symbolize(variables string, sources plugi...
  function fakeDemangler (line 487) | func fakeDemangler(name string) string {
  function longNameFuncsProfile (line 512) | func longNameFuncsProfile() *profile.Profile {
  function cpuProfile (line 587) | func cpuProfile() *profile.Profile {
  function cpuProfileSmall (line 706) | func cpuProfileSmall() *profile.Profile {
  function heapProfile (line 780) | func heapProfile() *profile.Profile {
  function contentionProfile (line 897) | func contentionProfile() *profile.Profile {
  function symzProfile (line 1000) | func symzProfile() *profile.Profile {
  function largeProfile (line 1035) | func largeProfile(tb testing.TB) *profile.Profile {
  function TestAutoComplete (line 1068) | func TestAutoComplete(t *testing.T) {
  function TestTagFilter (line 1078) | func TestTagFilter(t *testing.T) {
  function TestIdentifyNumLabelUnits (line 1159) | func TestIdentifyNumLabelUnits(t *testing.T) {
  function TestNumericTagFilter (line 1291) | func TestNumericTagFilter(t *testing.T) {
  function TestOptionsHaveHelp (line 1515) | func TestOptionsHaveHelp(t *testing.T) {
  type testSymbolzMergeFetcher (line 1530) | type testSymbolzMergeFetcher struct
    method Fetch (line 1532) | func (testSymbolzMergeFetcher) Fetch(s string, d, t time.Duration) (*p...
  function TestSymbolzAfterMerge (line 1550) | func TestSymbolzAfterMerge(t *testing.T) {
  function TestProfileCopier (line 1593) | func TestProfileCopier(t *testing.T) {
  type mockObjTool (line 1628) | type mockObjTool struct
    method Open (line 1630) | func (*mockObjTool) Open(file string, start, limit, offset uint64, rel...
    method Disasm (line 1634) | func (m *mockObjTool) Disasm(file string, start, end uint64, intelSynt...
  type mockFile (line 1659) | type mockFile struct
    method Name (line 1665) | func (m *mockFile) Name() string {
    method ObjAddr (line 1670) | func (m *mockFile) ObjAddr(addr uint64) (uint64, error) {
    method BuildID (line 1675) | func (m *mockFile) BuildID() string {
    method SourceLine (line 1683) | func (*mockFile) SourceLine(addr uint64) ([]plugin.Frame, error) {
    method Symbols (line 1738) | func (m *mockFile) Symbols(r *regexp.Regexp, addr uint64) ([]*plugin.S...
    method Close (line 1756) | func (*mockFile) Close() error {

FILE: internal/driver/fetch.go
  function fetchProfiles (line 41) | func fetchProfiles(s *source, o *plugin.Options) (*profile.Profile, erro...
  function grabSourcesAndBases (line 129) | func grabSourcesAndBases(sources, bases []profileSource, fetch plugin.Fe...
  function chunkedGrab (line 173) | func chunkedGrab(sources []profileSource, fetch plugin.Fetcher, obj plug...
  function concurrentGrab (line 207) | func concurrentGrab(sources []profileSource, fetch plugin.Fetcher, obj p...
  function combineProfiles (line 244) | func combineProfiles(profiles []*profile.Profile, msrcs []plugin.Mapping...
  type profileSource (line 277) | type profileSource struct
  function homeEnv (line 287) | func homeEnv() string {
  function setTmpDir (line 301) | func setTmpDir(ui plugin.UI) (string, error) {
  constant testSourceAddress (line 320) | testSourceAddress = "pproftest.local"
  function grabProfile (line 325) | func grabProfile(s *source, source string, fetcher plugin.Fetcher, obj p...
  function collectMappingSources (line 363) | func collectMappingSources(p *profile.Profile, source string) plugin.Map...
  function unsourceMappings (line 392) | func unsourceMappings(p *profile.Profile) {
  function locateBinaries (line 404) | func locateBinaries(p *profile.Profile, s *source, obj plugin.ObjTool, u...
  function fetch (line 494) | func fetch(source string, duration, timeout time.Duration, ui plugin.UI,...
  function fetchURL (line 523) | func fetchURL(source string, timeout time.Duration, tr http.RoundTripper...
  function statusCodeError (line 540) | func statusCodeError(resp *http.Response) error {
  function isPerfFile (line 552) | func isPerfFile(path string) bool {
  function convertPerfData (line 572) | func convertPerfData(perfPath string, ui plugin.UI) (*os.File, error) {
  function adjustURL (line 593) | func adjustURL(source string, duration, timeout time.Duration) (string, ...

FILE: internal/driver/fetch_test.go
  function TestSymbolizationPath (line 45) | func TestSymbolizationPath(t *testing.T) {
  function TestCollectMappingSources (line 100) | func TestCollectMappingSources(t *testing.T) {
  function TestUnsourceMappings (line 127) | func TestUnsourceMappings(t *testing.T) {
  type testObj (line 157) | type testObj struct
    method Open (line 161) | func (o testObj) Open(file string, start, limit, offset uint64, reloca...
    method Demangler (line 174) | func (testObj) Demangler(_ string) func(names []string) (map[string]st...
    method Disasm (line 177) | func (testObj) Disasm(file string, start, end uint64, intelSyntax bool...
  type testFile (line 181) | type testFile struct
    method Name (line 183) | func (f testFile) Name() string                                       ...
    method ObjAddr (line 184) | func (testFile) ObjAddr(addr uint64) (uint64, error)                  ...
    method BuildID (line 185) | func (f testFile) BuildID() string                                    ...
    method SourceLine (line 186) | func (testFile) SourceLine(addr uint64) ([]plugin.Frame, error)       ...
    method Symbols (line 187) | func (testFile) Symbols(r *regexp.Regexp, addr uint64) ([]*plugin.Sym,...
    method Close (line 188) | func (testFile) Close() error                                         ...
  function TestFetch (line 190) | func TestFetch(t *testing.T) {
  function TestFetchWithBase (line 245) | func TestFetchWithBase(t *testing.T) {
  function mappingSources (line 573) | func mappingSources(key, source string, start uint64) plugin.MappingSour...
  type httpTransport (line 584) | type httpTransport struct
    method RoundTrip (line 586) | func (tr *httpTransport) RoundTrip(req *http.Request) (*http.Response,...
  function closedError (line 601) | func closedError() string {
  function TestHTTPSInsecure (line 608) | func TestHTTPSInsecure(t *testing.T) {
  function TestHTTPSWithServerCertFetch (line 681) | func TestHTTPSWithServerCertFetch(t *testing.T) {
  function checkProfileHasFunction (line 794) | func checkProfileHasFunction(p *profile.Profile, fname string) error {
  function selfSignedCert (line 806) | func selfSignedCert(t *testing.T, host string) (tls.Certificate, []byte,...

FILE: internal/driver/flags.go
  type GoFlags (line 23) | type GoFlags struct
    method Bool (line 28) | func (*GoFlags) Bool(o string, d bool, c string) *bool {
    method Int (line 33) | func (*GoFlags) Int(o string, d int, c string) *int {
    method Float64 (line 38) | func (*GoFlags) Float64(o string, d float64, c string) *float64 {
    method String (line 43) | func (*GoFlags) String(o, d, c string) *string {
    method StringList (line 48) | func (*GoFlags) StringList(o, d, c string) *[]*string {
    method ExtraUsage (line 53) | func (f *GoFlags) ExtraUsage() string {
    method AddExtraUsage (line 58) | func (f *GoFlags) AddExtraUsage(eu string) {
    method Parse (line 63) | func (*GoFlags) Parse(usage func()) []string {

FILE: internal/driver/html/common.js
  function initPanAndZoom (line 3) | function initPanAndZoom(svg, clickHandler) {
  function initMenus (line 217) | function initMenus() {
  function sendURL (line 266) | function sendURL(method, url, done) {
  function initConfigManager (line 273) | function initConfigManager() {
  function viewer (line 396) | function viewer(baseUrl, nodes, options) {
  function pprofQuoteMeta (line 712) | function pprofQuoteMeta(str) {

FILE: internal/driver/html/stacks.js
  function stackViewer (line 4) | function stackViewer(stacks, nodes) {
  function pprofUnitText (line 615) | function pprofUnitText(value, unit) {

FILE: internal/driver/interactive.go
  function interactive (line 34) | func interactive(p *profile.Profile, o *plugin.Options) error {
  function greetings (line 128) | func greetings(p *profile.Profile, ui plugin.UI) {
  type shortcuts (line 144) | type shortcuts
    method expand (line 146) | func (a shortcuts) expand(input string) []string {
  function profileShortcuts (line 161) | func profileShortcuts(p *profile.Profile) shortcuts {
  function sampleTypes (line 173) | func sampleTypes(p *profile.Profile) []string {
  function printCurrentOptions (line 181) | func printCurrentOptions(p *profile.Profile, ui plugin.UI) {
  function parseCommandLine (line 223) | func parseCommandLine(input []string) ([]string, config, error) {
  function catRegex (line 309) | func catRegex(a, b string) string {
  function commandHelp (line 318) | func commandHelp(args string, ui plugin.UI) {
  function newCompleter (line 345) | func newCompleter(fns []string) func(string) string {
  function matchVariableOrCommand (line 381) | func matchVariableOrCommand(token string) string {
  function functionCompleter (line 400) | func functionCompleter(substring string, fns []string) string {
  function functionNames (line 416) | func functionNames(p *profile.Profile) []string {

FILE: internal/driver/interactive_test.go
  function TestShell (line 30) | func TestShell(t *testing.T) {
  function makeShortcuts (line 108) | func makeShortcuts(input []string, seed int64) (shortcuts, []string) {
  function checkValue (line 134) | func checkValue(p *profile.Profile, cmd []string, cfg config, o *plugin....
  function interleave (line 160) | func interleave(input []string, seed int64) []string {
  function TestInteractiveCommands (line 179) | func TestInteractiveCommands(t *testing.T) {

FILE: internal/driver/options.go
  function setDefaults (line 32) | func setDefaults(o *plugin.Options) *plugin.Options {
  type stdUI (line 58) | type stdUI struct
    method ReadLine (line 62) | func (ui *stdUI) ReadLine(prompt string) (string, error) {
    method Print (line 67) | func (ui *stdUI) Print(args ...interface{}) {
    method PrintErr (line 71) | func (ui *stdUI) PrintErr(args ...interface{}) {
    method IsTerminal (line 75) | func (ui *stdUI) IsTerminal() bool {
    method WantBrowser (line 79) | func (ui *stdUI) WantBrowser() bool {
    method SetAutoComplete (line 83) | func (ui *stdUI) SetAutoComplete(func(string) string) {
    method fprint (line 86) | func (ui *stdUI) fprint(f *os.File, args []interface{}) {
  type oswriter (line 95) | type oswriter struct
    method Open (line 97) | func (oswriter) Open(name string) (io.WriteCloser, error) {

FILE: internal/driver/settings.go
  type settings (line 12) | type settings struct
  type namedConfig (line 18) | type namedConfig struct
  function settingsFileName (line 24) | func settingsFileName() (string, error) {
  function readSettings (line 34) | func readSettings(fname string) (*settings, error) {
  function writeSettings (line 53) | func writeSettings(fname string, settings *settings) error {
  type configMenuEntry (line 73) | type configMenuEntry struct
  function configMenu (line 81) | func configMenu(fname string, u url.URL) []configMenuEntry {
  function editSettings (line 113) | func editSettings(fname string, fn func(s *settings) error) error {
  function setConfig (line 125) | func setConfig(fname string, request url.URL) error {
  function removeConfig (line 148) | func removeConfig(fname, config string) error {

FILE: internal/driver/settings_test.go
  function settingsDirAndFile (line 14) | func settingsDirAndFile(t *testing.T) (string, string) {
  function TestSettings (line 22) | func TestSettings(t *testing.T) {
  function TestParseConfig (line 58) | func TestParseConfig(t *testing.T) {
  function TestDefaultConfig (line 108) | func TestDefaultConfig(t *testing.T) {
  function TestConfigMenu (line 119) | func TestConfigMenu(t *testing.T) {
  function TestEditConfig (line 147) | func TestEditConfig(t *testing.T) {
  function TestAssign (line 215) | func TestAssign(t *testing.T) {

FILE: internal/driver/stacks.go
  method stackView (line 26) | func (ui *webInterface) stackView(w http.ResponseWriter, req *http.Reque...

FILE: internal/driver/svg.go
  function massageSVG (line 33) | func massageSVG(svg string) string {

FILE: internal/driver/tagroot.go
  function addLabelNodes (line 17) | func addLabelNodes(p *profile.Profile, rootKeys, leafKeys []string, outp...
  function formatLabelValues (line 115) | func formatLabelValues(s *profile.Sample, k string, outputUnit string) [...

FILE: internal/driver/tagroot_test.go
  constant mainBinary (line 12) | mainBinary = "/bin/main"
  function TestAddLabelNodesMatchBooleans (line 178) | func TestAddLabelNodesMatchBooleans(t *testing.T) {
  function stackCollapse (line 372) | func stackCollapse(p *profile.Profile) []string {

FILE: internal/driver/tempfile.go
  function newTempFile (line 25) | func newTempFile(dir, prefix, suffix string) (*os.File, error) {
  function deferDeleteTempFile (line 42) | func deferDeleteTempFile(path string) {
  function cleanupTempFiles (line 49) | func cleanupTempFiles() error {

FILE: internal/driver/tempfile_test.go
  function TestNewTempFile (line 9) | func TestNewTempFile(t *testing.T) {

FILE: internal/driver/webhtml.go
  function getHTMLTemplates (line 34) | func getHTMLTemplates() *template.Template {
  function addTemplates (line 47) | func addTemplates(templates *template.Template) {

FILE: internal/driver/webui.go
  type webInterface (line 41) | type webInterface struct
    method makeReport (line 261) | func (ui *webInterface) makeReport(w http.ResponseWriter, req *http.Re...
    method render (line 297) | func (ui *webInterface) render(w http.ResponseWriter, req *http.Reques...
    method dot (line 313) | func (ui *webInterface) dot(w http.ResponseWriter, req *http.Request) {
    method top (line 365) | func (ui *webInterface) top(w http.ResponseWriter, req *http.Request) {
    method disasm (line 385) | func (ui *webInterface) disasm(w http.ResponseWriter, req *http.Reques...
    method source (line 408) | func (ui *webInterface) source(w http.ResponseWriter, req *http.Reques...
    method peek (line 430) | func (ui *webInterface) peek(w http.ResponseWriter, req *http.Request) {
    method saveConfig (line 453) | func (ui *webInterface) saveConfig(w http.ResponseWriter, req *http.Re...
    method deleteConfig (line 462) | func (ui *webInterface) deleteConfig(w http.ResponseWriter, req *http....
  function makeWebInterface (line 49) | func makeWebInterface(p *profile.Profile, copier profileCopier, opt *plu...
  constant maxEntries (line 64) | maxEntries = 50
  type errorCatcher (line 67) | type errorCatcher struct
    method PrintErr (line 72) | func (ec *errorCatcher) PrintErr(args ...interface{}) {
  type webArgs (line 78) | type webArgs struct
  function serveWebInterface (line 98) | func serveWebInterface(hostport string, p *profile.Profile, o *plugin.Op...
  function getHostAndPort (line 158) | func getHostAndPort(hostport string) (string, int, error) {
  function defaultWebServer (line 185) | func defaultWebServer(args *plugin.HTTPServerArgs) error {
  function redirectWithQuery (line 223) | func redirectWithQuery(path string, code int) http.HandlerFunc {
  function isLocalhost (line 231) | func isLocalhost(host string) bool {
  function openBrowser (line 235) | func openBrowser(url string, o *plugin.Options) {
  function renderHTML (line 285) | func renderHTML(dst io.Writer, tmpl string, rpt *report.Report, errList,...
  function dotToSvg (line 347) | func dotToSvg(dot []byte) ([]byte, error) {
  function getFromLegend (line 473) | func getFromLegend(legend []string, param, def string) string {

FILE: internal/driver/webui_test.go
  function makeTestServer (line 35) | func makeTestServer(t testing.TB, prof *profile.Profile) *httptest.Server {
  function TestWebInterface (line 68) | func TestWebInterface(t *testing.T) {
  constant addrBase (line 158) | addrBase = 0x1000
  constant fakeSource (line 159) | fakeSource = "testdata/file1000.src"
  type fakeObj (line 161) | type fakeObj struct
    method Close (line 163) | func (f fakeObj) Close() error                        { return nil }
    method Name (line 164) | func (f fakeObj) Name() string                        { return "testbi...
    method ObjAddr (line 165) | func (f fakeObj) ObjAddr(addr uint64) (uint64, error) { return addr, n...
    method BuildID (line 166) | func (f fakeObj) BuildID() string                     { return "" }
    method SourceLine (line 167) | func (f fakeObj) SourceLine(addr uint64) ([]plugin.Frame, error) {
    method Symbols (line 170) | func (f fakeObj) Symbols(r *regexp.Regexp, addr uint64) ([]*plugin.Sym...
  type fakeObjTool (line 187) | type fakeObjTool struct
    method Open (line 189) | func (obj fakeObjTool) Open(file string, start, limit, offset uint64, ...
    method Disasm (line 193) | func (obj fakeObjTool) Disasm(file string, start, end uint64, intelSyn...
  function makeFakeProfile (line 201) | func makeFakeProfile() *profile.Profile {
  function TestGetHostAndPort (line 257) | func TestGetHostAndPort(t *testing.T) {
  function TestIsLocalHost (line 292) | func TestIsLocalHost(t *testing.T) {
  function BenchmarkTop (line 305) | func BenchmarkTop(b *testing.B)   { benchmarkURL(b, "/top", false) }
  function BenchmarkFlame (line 306) | func BenchmarkFlame(b *testing.B) { benchmarkURL(b, "/flamegraph", false) }
  function BenchmarkDot (line 307) | func BenchmarkDot(b *testing.B)   { benchmarkURL(b, "/", true) }
  function benchmarkURL (line 309) | func benchmarkURL(b *testing.B, path string, needDot bool) {

FILE: internal/elfexec/elfexec.go
  constant maxNoteSize (line 27) | maxNoteSize        = 1 << 20
  constant noteTypeGNUBuildID (line 28) | noteTypeGNUBuildID = 3
  type elfNote (line 32) | type elfNote struct
  function parseNotes (line 39) | func parseNotes(reader io.Reader, alignment int, order binary.ByteOrder)...
  function GetBuildID (line 121) | func GetBuildID(f *elf.File) ([]byte, error) {
  function kernelBase (line 167) | func kernelBase(loadSegment *elf.ProgHeader, stextOffset *uint64, start,...
  function GetBase (line 216) | func GetBase(fh *elf.FileHeader, loadSegment *elf.ProgHeader, stextOffse...
  function FindTextProgHeader (line 287) | func FindTextProgHeader(f *elf.File) *elf.ProgHeader {
  function ProgramHeadersForMapping (line 310) | func ProgramHeadersForMapping(phdrs []elf.ProgHeader, mapOff, mapSz uint...
  function HeaderForFileOffset (line 358) | func HeaderForFileOffset(headers []*elf.ProgHeader, fileOffset uint64) (...

FILE: internal/elfexec/elfexec_test.go
  function TestGetBase (line 25) | func TestGetBase(t *testing.T) {
  function uint64p (line 116) | func uint64p(n uint64) *uint64 {
  function TestFindProgHeaderForMapping (line 120) | func TestFindProgHeaderForMapping(t *testing.T) {
  function TestHeaderForFileOffset (line 413) | func TestHeaderForFileOffset(t *testing.T) {

FILE: internal/graph/dotgraph.go
  type DotAttributes (line 29) | type DotAttributes struct
  type DotNodeAttributes (line 34) | type DotNodeAttributes struct
  type DotConfig (line 44) | type DotConfig struct
  constant maxNodelets (line 53) | maxNodelets = 4
  function ComposeDot (line 57) | func ComposeDot(w io.Writer, g *Graph, a *DotAttributes, c *DotConfig) {
  type builder (line 101) | type builder struct
    method start (line 108) | func (b *builder) start() {
    method finish (line 118) | func (b *builder) finish() {
    method addLegend (line 123) | func (b *builder) addLegend() {
    method addNode (line 141) | func (b *builder) addNode(node *Node, nodeID int, maxFlat float64) {
    method addNodelets (line 216) | func (b *builder) addNodelets(node *Node, nodeID int) bool {
    method numericNodelets (line 265) | func (b *builder) numericNodelets(nts []*Tag, maxNumNodelets int, flat...
    method addEdge (line 285) | func (b *builder) addEdge(edge *Edge, from, to int, hasNodelets bool) {
    method collapsedTags (line 399) | func (b *builder) collapsedTags(ts []*Tag, count int, flatTags bool) [...
    method tagGroupLabel (line 439) | func (b *builder) tagGroupLabel(g []*Tag) (label string, flat, cum int...
  function dotColor (line 330) | func dotColor(score float64, isBackground bool) string {
  function multilinePrintableName (line 384) | func multilinePrintableName(info *NodeInfo) string {
  function tagDistance (line 431) | func tagDistance(t, u *Tag) float64 {
  function min64 (line 473) | func min64(a, b int64) int64 {
  function escapeAllForDot (line 481) | func escapeAllForDot(in []string) []string {
  function escapeForDot (line 492) | func escapeForDot(str string) string {

FILE: internal/graph/dotgraph_test.go
  function TestComposeWithStandardGraph (line 33) | func TestComposeWithStandardGraph(t *testing.T) {
  function TestComposeWithNodeAttributesAndZeroFlat (line 43) | func TestComposeWithNodeAttributesAndZeroFlat(t *testing.T) {
  function TestComposeWithTagsAndResidualEdge (line 67) | func TestComposeWithTagsAndResidualEdge(t *testing.T) {
  function TestComposeWithNestedTags (line 95) | func TestComposeWithNestedTags(t *testing.T) {
  function TestComposeWithEmptyGraph (line 120) | func TestComposeWithEmptyGraph(t *testing.T) {
  function TestComposeWithStandardGraphAndURL (line 130) | func TestComposeWithStandardGraphAndURL(t *testing.T) {
  function TestComposeWithNamesThatNeedEscaping (line 141) | func TestComposeWithNamesThatNeedEscaping(t *testing.T) {
  function TestComposeWithCommentsWithNewlines (line 153) | func TestComposeWithCommentsWithNewlines(t *testing.T) {
  function baseGraph (line 166) | func baseGraph() *Graph {
  function baseAttrsAndConfig (line 200) | func baseAttrsAndConfig() (*DotAttributes, *DotConfig) {
  function compareGraphs (line 215) | func compareGraphs(t *testing.T, got []byte, wantFile string) {
  function TestNodeletCountCapping (line 237) | func TestNodeletCountCapping(t *testing.T) {
  function TestMultilinePrintableName (line 290) | func TestMultilinePrintableName(t *testing.T) {
  function TestTagCollapse (line 304) | func TestTagCollapse(t *testing.T) {
  function TestEscapeForDot (line 354) | func TestEscapeForDot(t *testing.T) {
  function tagString (line 394) | func tagString(t []*Tag) string {

FILE: internal/graph/graph.go
  type Graph (line 50) | type Graph struct
    method TrimTree (line 479) | func (g *Graph) TrimTree(kept NodePtrSet) {
    method String (line 726) | func (g *Graph) String() string {
    method DiscardLowFrequencyNodes (line 752) | func (g *Graph) DiscardLowFrequencyNodes(nodeCutoff int64) NodeSet {
    method DiscardLowFrequencyNodePtrs (line 758) | func (g *Graph) DiscardLowFrequencyNodePtrs(nodeCutoff int64) NodePtrS...
    method TrimLowFrequencyTags (line 791) | func (g *Graph) TrimLowFrequencyTags(tagCutoff int64) {
    method TrimLowFrequencyEdges (line 813) | func (g *Graph) TrimLowFrequencyEdges(edgeCutoff int64) int {
    method SortNodes (line 828) | func (g *Graph) SortNodes(cum bool, visualMode bool) {
    method SelectTopNodePtrs (line 842) | func (g *Graph) SelectTopNodePtrs(maxNodes int, visualMode bool) NodeP...
    method SelectTopNodes (line 851) | func (g *Graph) SelectTopNodes(maxNodes int, visualMode bool) NodeSet {
    method selectTopNodes (line 856) | func (g *Graph) selectTopNodes(maxNodes int, visualMode bool) Nodes {
    method RemoveRedundantEdges (line 899) | func (g *Graph) RemoveRedundantEdges() {
  type Options (line 55) | type Options struct
  type Nodes (line 69) | type Nodes
    method Sum (line 649) | func (ns Nodes) Sum() (flat int64, cum int64) {
    method Sort (line 958) | func (ns Nodes) Sort(o NodeOrder) error {
  type Node (line 73) | type Node struct
    method FlatValue (line 104) | func (n *Node) FlatValue() int64 {
    method CumValue (line 113) | func (n *Node) CumValue() int64 {
    method AddToEdge (line 122) | func (n *Node) AddToEdge(to *Node, v int64, residual, inline bool) {
    method AddToEdgeDiv (line 128) | func (n *Node) AddToEdgeDiv(to *Node, dv, v int64, residual, inline bo...
    method addSample (line 657) | func (n *Node) addSample(dw, w int64, labels string, numLabel map[stri...
  type NodeInfo (line 151) | type NodeInfo struct
    method PrintableName (line 162) | func (i *NodeInfo) PrintableName() string {
    method NameComponents (line 167) | func (i *NodeInfo) NameComponents() []string {
    method comparePrintableName (line 200) | func (i *NodeInfo) comparePrintableName(right NodeInfo) (equal bool, l...
  type NodeMap (line 216) | type NodeMap
    method FindOrInsertNode (line 234) | func (nm NodeMap) FindOrInsertNode(info NodeInfo, kept NodeSet) *Node {
    method nodes (line 587) | func (nm NodeMap) nodes() Nodes {
    method findOrInsertLine (line 595) | func (nm NodeMap) findOrInsertLine(l *profile.Location, li profile.Lin...
  type NodeSet (line 219) | type NodeSet
  type NodePtrSet (line 229) | type NodePtrSet
  type EdgeMap (line 268) | type EdgeMap
    method Sort (line 1136) | func (e EdgeMap) Sort() []*Edge {
    method Sum (line 1147) | func (e EdgeMap) Sum() int64 {
  type Edge (line 271) | type Edge struct
    method WeightValue (line 285) | func (e *Edge) WeightValue() int64 {
  type Tag (line 293) | type Tag struct
    method FlatValue (line 303) | func (t *Tag) FlatValue() int64 {
    method CumValue (line 312) | func (t *Tag) CumValue() int64 {
  type TagMap (line 320) | type TagMap
    method findOrAddTag (line 712) | func (m TagMap) findOrAddTag(label, unit string, value int64) *Tag {
  function SortTags (line 323) | func SortTags(t []*Tag, flat bool) []*Tag {
  function New (line 330) | func New(prof *profile.Profile, o *Options) *Graph {
  function newGraph (line 341) | func newGraph(prof *profile.Profile, o *Options) (*Graph, map[uint64]Nod...
  function selectNodesForGraph (line 394) | func selectNodesForGraph(nodes Nodes, dropNegative bool) *Graph {
  type nodePair (line 412) | type nodePair struct
  function newTree (line 416) | func newTree(prof *profile.Profile, o *Options) (g *Graph) {
  function ShortenFunctionName (line 466) | func ShortenFunctionName(f string) string {
  function joinLabels (line 539) | func joinLabels(s *profile.Sample) string {
  function isNegative (line 556) | func isNegative(n *Node) bool {
  function CreateNodes (line 570) | func CreateNodes(prof *profile.Profile, o *Options) (Nodes, map[uint64]N...
  function nodeInfo (line 606) | func nodeInfo(l *profile.Location, line profile.Line, objfile string, o ...
  type tags (line 629) | type tags struct
    method Len (line 634) | func (t tags) Len() int      { return len(t.t) }
    method Swap (line 635) | func (t tags) Swap(i, j int) { t.t[i], t.t[j] = t.t[j], t.t[i] }
    method Less (line 636) | func (t tags) Less(i, j int) bool {
  function defaultLabelFormat (line 708) | func defaultLabelFormat(v int64, key string) string {
  function makeNodeSet (line 767) | func makeNodeSet(nodes Nodes, nodeCutoff int64) NodeSet {
  function getNodesAboveCumCutoff (line 778) | func getNodesAboveCumCutoff(nodes Nodes, nodeCutoff int64) Nodes {
  function trimLowFreqTags (line 801) | func trimLowFreqTags(tags TagMap, minValue int64) TagMap {
  function countTags (line 879) | func countTags(n *Node) int {
  function isRedundantEdge (line 922) | func isRedundantEdge(e *Edge) bool {
  type nodeSorter (line 945) | type nodeSorter struct
    method Len (line 950) | func (s nodeSorter) Len() int           { return len(s.rs) }
    method Swap (line 951) | func (s nodeSorter) Swap(i, j int)      { s.rs[i], s.rs[j] = s.rs[j], ...
    method Less (line 952) | func (s nodeSorter) Less(i, j int) bool { return s.less(s.rs[i], s.rs[...
  function compareNodes (line 1064) | func compareNodes(l, r *Node) bool {
  function entropyScore (line 1075) | func entropyScore(n *Node) int64 {
  function edgeEntropyScore (line 1098) | func edgeEntropyScore(n *Node, edges EdgeMap, self int64) float64 {
  type NodeOrder (line 1120) | type NodeOrder
  constant FlatNameOrder (line 1124) | FlatNameOrder NodeOrder = iota
  constant FlatCumNameOrder (line 1125) | FlatCumNameOrder
  constant CumNameOrder (line 1126) | CumNameOrder
  constant NameOrder (line 1127) | NameOrder
  constant FileOrder (line 1128) | FileOrder
  constant AddressOrder (line 1129) | AddressOrder
  constant EntropyOrder (line 1130) | EntropyOrder
  type edgeList (line 1155) | type edgeList
    method Len (line 1157) | func (el edgeList) Len() int {
    method Less (line 1161) | func (el edgeList) Less(i, j int) bool {
    method Swap (line 1178) | func (el edgeList) Swap(i, j int) {
  function abs64 (line 1182) | func abs64(i int64) int64 {

FILE: internal/graph/graph_test.go
  function edgeDebugString (line 24) | func edgeDebugString(edge *Edge) string {
  function edgeMapsDebugString (line 34) | func edgeMapsDebugString(in, out EdgeMap) string {
  function graphDebugString (line 49) | func graphDebugString(graph *Graph) string {
  function expectedNodesDebugString (line 63) | func expectedNodesDebugString(expected []expectedNode) string {
  function edgeMapsEqual (line 78) | func edgeMapsEqual(this, that EdgeMap) bool {
  function nodesEqual (line 91) | func nodesEqual(node *Node, expected expectedNode) bool {
  function graphsEqual (line 97) | func graphsEqual(graph *Graph, expected []expectedNode) bool {
  type expectedNode (line 115) | type expectedNode struct
  type trimTreeTestcase (line 120) | type trimTreeTestcase struct
  function makeExpectedEdgeResidual (line 127) | func makeExpectedEdgeResidual(parent, child expectedNode) {
  function makeEdgeInline (line 132) | func makeEdgeInline(edgeMap EdgeMap, node *Node) {
  function setEdgeWeight (line 136) | func setEdgeWeight(edgeMap EdgeMap, node *Node, weight int64) {
  function createEdges (line 141) | func createEdges(parent *Node, children ...*Node) {
  function createEmptyNode (line 153) | func createEmptyNode() *Node {
  function createExpectedNodes (line 161) | func createExpectedNodes(nodes ...*Node) ([]expectedNode, NodePtrSet) {
  function createExpectedEdges (line 179) | func createExpectedEdges(parent expectedNode, children ...expectedNode) {
  function createTestCase1 (line 203) | func createTestCase1() trimTreeTestcase {
  function createTestCase2 (line 250) | func createTestCase2() trimTreeTestcase {
  function createTestCase3 (line 280) | func createTestCase3() trimTreeTestcase {
  function createTestCase4 (line 297) | func createTestCase4() trimTreeTestcase {
  function createTrimTreeTestCases (line 311) | func createTrimTreeTestCases() []trimTreeTestcase {
  function TestTrimTree (line 325) | func TestTrimTree(t *testing.T) {
  function nodeTestProfile (line 338) | func nodeTestProfile() *profile.Profile {
  function TestCreateNodes (line 403) | func TestCreateNodes(t *testing.T) {
  function TestShortenFunctionName (line 422) | func TestShortenFunctionName(t *testing.T) {

FILE: internal/measurement/measurement.go
  function ScaleProfiles (line 31) | func ScaleProfiles(profiles []*profile.Profile) error {
  function CommonValueType (line 88) | func CommonValueType(ts []*profile.ValueType) (*profile.ValueType, error) {
  function compatibleValueTypes (line 105) | func compatibleValueTypes(v1, v2 *profile.ValueType) bool {
  function Scale (line 128) | func Scale(value int64, fromUnit, toUnit string) (float64, string) {
  function Label (line 149) | func Label(value int64, unit string) string {
  function ScaledLabel (line 155) | func ScaledLabel(value int64, fromUnit, toUnit string) string {
  function Percentage (line 166) | func Percentage(value, total int64) string {
  type Unit (line 184) | type Unit struct
  type UnitType (line 192) | type UnitType struct
    method findByAlias (line 199) | func (ut UnitType) findByAlias(alias string) *Unit {
    method sniffUnit (line 210) | func (ut UnitType) sniffUnit(unit string) *Unit {
    method autoScale (line 221) | func (ut UnitType) autoScale(value float64) (float64, string, bool) {
    method convertUnit (line 241) | func (ut UnitType) convertUnit(value int64, fromUnitStr, toUnitStr str...

FILE: internal/measurement/measurement_test.go
  function TestScale (line 22) | func TestScale(t *testing.T) {
  function floatEqual (line 72) | func floatEqual(a, b float64) bool {

FILE: internal/plugin/plugin.go
  type Options (line 28) | type Options struct
  type Writer (line 50) | type Writer interface
  type FlagSet (line 56) | type FlagSet interface
  type Fetcher (line 92) | type Fetcher interface
  type Symbolizer (line 97) | type Symbolizer interface
  type MappingSources (line 103) | type MappingSources
  type ObjTool (line 109) | type ObjTool interface
  type Inst (line 123) | type Inst struct
  type ObjFile (line 132) | type ObjFile interface
  type Frame (line 161) | type Frame struct
  type Sym (line 170) | type Sym struct
  type UI (line 178) | type UI interface
  type HTTPServerArgs (line 208) | type HTTPServerArgs struct

FILE: internal/proftest/proftest.go
  function Diff (line 37) | func Diff(b1, b2 []byte) (data []byte, err error) {
  function EncodeJSON (line 70) | func EncodeJSON(x interface{}) []byte {
  type TestUI (line 83) | type TestUI struct
    method ReadLine (line 93) | func (ui *TestUI) ReadLine(_ string) (string, error) {
    method Print (line 106) | func (ui *TestUI) Print(args ...interface{}) {
    method PrintErr (line 111) | func (ui *TestUI) PrintErr(args ...interface{}) {
    method IsTerminal (line 133) | func (ui *TestUI) IsTerminal() bool {
    method WantBrowser (line 138) | func (ui *TestUI) WantBrowser() bool {
    method SetAutoComplete (line 143) | func (ui *TestUI) SetAutoComplete(_ func(string) string) {
  function LargeProfile (line 151) | func LargeProfile(tb testing.TB) []byte {

FILE: internal/report/package.go
  function packageName (line 11) | func packageName(name string) string {

FILE: internal/report/package_test.go
  function TestPackageName (line 7) | func TestPackageName(t *testing.T) {

FILE: internal/report/report.go
  constant Callgrind (line 39) | Callgrind = iota
  constant Comments (line 40) | Comments
  constant Dis (line 41) | Dis
  constant Dot (line 42) | Dot
  constant List (line 43) | List
  constant Proto (line 44) | Proto
  constant Raw (line 45) | Raw
  constant Tags (line 46) | Tags
  constant Text (line 47) | Text
  constant TopProto (line 48) | TopProto
  constant Traces (line 49) | Traces
  constant Tree (line 50) | Tree
  constant WebList (line 51) | WebList
  type Options (line 56) | type Options struct
  function Generate (line 88) | func Generate(w io.Writer, rpt *Report, obj plugin.ObjTool) error {
  function printProto (line 298) | func printProto(w io.Writer, rpt *Report) error {
  function printTopProto (line 313) | func printTopProto(w io.Writer, rpt *Report) error {
  type functionMap (line 361) | type functionMap
    method findOrAdd (line 367) | func (fm functionMap) findOrAdd(ni graph.NodeInfo) (*profile.Function,...
  function printAssembly (line 386) | func printAssembly(w io.Writer, rpt *Report, obj plugin.ObjTool) error {
  function PrintAssembly (line 391) | func PrintAssembly(w io.Writer, rpt *Report, obj plugin.ObjTool, maxFunc...
  function symbolsFromBinaries (line 520) | func symbolsFromBinaries(prof *profile.Profile, g *graph.Graph, rx *rege...
  type objSymbol (line 575) | type objSymbol struct
  type orderSyms (line 581) | type orderSyms struct
    method Len (line 586) | func (o orderSyms) Len() int           { return len(o.v) }
    method Less (line 587) | func (o orderSyms) Less(i, j int) bool { return o.less(o.v[i], o.v[j]) }
    method Swap (line 588) | func (o orderSyms) Swap(i, j int)      { o.v[i], o.v[j] = o.v[j], o.v[...
  function nodesPerSymbol (line 591) | func nodesPerSymbol(ns graph.Nodes, symbols []*objSymbol) map[*objSymbol...
  type assemblyInstruction (line 604) | type assemblyInstruction struct
    method flatValue (line 621) | func (a *assemblyInstruction) flatValue() int64 {
    method cumValue (line 628) | func (a *assemblyInstruction) cumValue() int64 {
  type callID (line 616) | type callID struct
  function annotateAssembly (line 638) | func annotateAssembly(insts []plugin.Inst, samples graph.Nodes, file plu...
  function valueOrDot (line 689) | func valueOrDot(value int64, rpt *Report) string {
  function printTags (line 698) | func printTags(w io.Writer, rpt *Report) error {
  function printComments (line 770) | func printComments(w io.Writer, rpt *Report) error {
  type TextItem (line 780) | type TextItem struct
  function TextItems (line 789) | func TextItems(rpt *Report) ([]TextItem, []string) {
  function printText (line 831) | func printText(w io.Writer, rpt *Report) error {
  function printTraces (line 853) | func printTraces(w io.Writer, rpt *Report) error {
  function printCallgrind (line 929) | func printCallgrind(w io.Writer, rpt *Report) error {
  function getDisambiguatedNames (line 990) | func getDisambiguatedNames(g *graph.Graph) map[*graph.Node]string {
  function callgrindName (line 1029) | func callgrindName(names map[string]int, name string) string {
  function callgrindAddress (line 1045) | func callgrindAddress(prevInfo *graph.NodeInfo, curr uint64) string {
  function printTree (line 1068) | func printTree(w io.Writer, rpt *Report) error {
  function GetDOT (line 1136) | func GetDOT(rpt *Report) (*graph.Graph, *graph.DotConfig) {
  function printDOT (line 1151) | func printDOT(w io.Writer, rpt *Report) error {
  function ProfileLabels (line 1158) | func ProfileLabels(rpt *Report) []string {
  function graphTotal (line 1198) | func graphTotal(g *graph.Graph) int64 {
  function reportLabels (line 1208) | func reportLabels(rpt *Report, shownTotal int64, nodeCount, origCount, d...
  function legendActiveFilters (line 1250) | func legendActiveFilters(activeFilters []string) []string {
  function genLabel (line 1262) | func genLabel(d int, n, l, f string) string {
  function New (line 1271) | func New(prof *profile.Profile, o *Options) *Report {
  function NewDefault (line 1285) | func NewDefault(prof *profile.Profile, options Options) *Report {
  function computeTotal (line 1302) | func computeTotal(prof *profile.Profile, value, meanDiv func(v []int64) ...
  type Report (line 1332) | type Report struct
    method newTrimmedGraph (line 124) | func (rpt *Report) newTrimmedGraph() (g *graph.Graph, origCount, dropp...
    method selectOutputUnit (line 187) | func (rpt *Report) selectOutputUnit(g *graph.Graph) {
    method newGraph (line 238) | func (rpt *Report) newGraph(nodes graph.NodeSet) *graph.Graph {
    method Total (line 1340) | func (rpt *Report) Total() int64 { return rpt.total }
    method OutputFormat (line 1343) | func (rpt *Report) OutputFormat() int { return rpt.options.OutputFormat }
    method DocURL (line 1346) | func (rpt *Report) DocURL() string {
  function absoluteURL (line 1354) | func absoluteURL(str string) bool {
  function abs64 (line 1361) | func abs64(i int64) int64 {

FILE: internal/report/report_test.go
  type testcase (line 35) | type testcase struct
  function TestSource (line 40) | func TestSource(t *testing.T) {
  function TestFilter (line 107) | func TestFilter(t *testing.T) {
  function testSample (line 256) | func testSample(value int64, locs ...*profile.Location) *profile.Sample {
  function makeTestProfile (line 265) | func makeTestProfile(samples ...*profile.Sample) *profile.Profile {
  function TestDisambiguation (line 312) | func TestDisambiguation(t *testing.T) {
  function TestFunctionMap (line 342) | func TestFunctionMap(t *testing.T) {
  function TestLegendActiveFilters (line 373) | func TestLegendActiveFilters(t *testing.T) {
  function TestComputeTotal (line 394) | func TestComputeTotal(t *testing.T) {
  function TestPrintAssemblyErrorMessage (line 512) | func TestPrintAssemblyErrorMessage(t *testing.T) {
  function TestDocURL (line 554) | func TestDocURL(t *testing.T) {
  function TestDocURLInLabels (line 583) | func TestDocURLInLabels(t *testing.T) {
  function TestProfileLabels (line 601) | func TestProfileLabels(t *testing.T) {
  function BenchmarkReportNewTrimmedGraph (line 619) | func BenchmarkReportNewTrimmedGraph(b *testing.B) {

FILE: internal/report/shortnames.go
  function fileNameSuffixes (line 31) | func fileNameSuffixes(name string) []string {
  function shortNameList (line 41) | func shortNameList(name string) []string {
  function allSuffixes (line 48) | func allSuffixes(name string, re *regexp.Regexp) []string {

FILE: internal/report/shortnames_test.go
  function TestShortNames (line 8) | func TestShortNames(t *testing.T) {
  function TestFileNameSuffixes (line 36) | func TestFileNameSuffixes(t *testing.T) {

FILE: internal/report/source.go
  function printSource (line 43) | func printSource(w io.Writer, rpt *Report) error {
  type sourcePrinter (line 127) | type sourcePrinter struct
    method close (line 363) | func (sp *sourcePrinter) close() {
    method expandAddresses (line 371) | func (sp *sourcePrinter) expandAddresses(rpt *Report, addrs map[uint64...
    method addStack (line 465) | func (sp *sourcePrinter) addStack(addr uint64, frames []plugin.Frame) {
    method handleUnprocessed (line 506) | func (sp *sourcePrinter) handleUnprocessed(addrs map[uint64]addrInfo, ...
    method splitIntoRanges (line 547) | func (sp *sourcePrinter) splitIntoRanges(prof *profile.Profile, addrMa...
    method initSamples (line 594) | func (sp *sourcePrinter) initSamples(flat, cum map[uint64]int64) {
    method generate (line 608) | func (sp *sourcePrinter) generate(maxFiles int, rpt *Report) WebListDa...
    method generateFile (line 651) | func (sp *sourcePrinter) generateFile(f *sourceFile, rpt *Report) WebL...
    method functions (line 717) | func (sp *sourcePrinter) functions(f *sourceFile) []sourceFunction {
    method objectFile (line 790) | func (sp *sourcePrinter) objectFile(m *profile.Mapping) plugin.ObjFile {
  type addrInfo (line 145) | type addrInfo struct
  type instructionInfo (line 151) | type instructionInfo struct
  type sourceFile (line 161) | type sourceFile struct
  type sourceInst (line 170) | type sourceInst struct
  type sourceFunction (line 177) | type sourceFunction struct
  type addressRange (line 184) | type addressRange struct
  type WebListData (line 192) | type WebListData struct
  type WebListFile (line 198) | type WebListFile struct
  type WebListFunc (line 203) | type WebListFunc struct
  type WebListLine (line 213) | type WebListLine struct
  type WebListInstruction (line 223) | type WebListInstruction struct
  type WebListCall (line 235) | type WebListCall struct
  function MakeWebList (line 243) | func MakeWebList(rpt *Report, obj plugin.ObjTool, maxFiles int) (WebList...
  function newSourcePrinter (line 260) | func newSourcePrinter(rpt *Report, obj plugin.ObjTool, sourcePath string...
  constant synthAsm (line 502) | synthAsm = ""
  function makeWebListLine (line 807) | func makeWebListLine(lineNo int, flat, cum int64, lineContents string,
  function makeWebListInstructions (line 837) | func makeWebListInstructions(srcIndent int, assembly []assemblyInstructi...
  function getSourceFromFile (line 884) | func getSourceFromFile(file string, reader *sourceReader, fns graph.Node...
  type sourceReader (line 943) | type sourceReader struct
    method fileError (line 969) | func (reader *sourceReader) fileError(path string) error {
    method line (line 974) | func (reader *sourceReader) line(path string, lineno int) (string, boo...
  function newSourceReader (line 960) | func newSourceReader(searchPath, trimPath string) *sourceReader {
  function openSourceFile (line 1006) | func openSourceFile(path, searchPath, trim string) (*os.File, error) {
  function trimPath (line 1036) | func trimPath(path, trimPath, searchPath string) string {
  function indentation (line 1066) | func indentation(line string) int {
  function rightPad (line 1087) | func rightPad(s string, n int) string {
  function canonicalizeFileName (line 1112) | func canonicalizeFileName(fname string) string {

FILE: internal/report/source_html.go
  function AddSourceTemplates (line 22) | func AddSourceTemplates(t *template.Template) {
  constant weblistPageCSS (line 27) | weblistPageCSS = `<style type="text/css">
  constant weblistPageScript (line 55) | weblistPageScript = `<script type="text/javascript">

FILE: internal/report/source_test.go
  function TestWebList (line 30) | func TestWebList(t *testing.T) {
  function TestSourceSyntheticAddress (line 55) | func TestSourceSyntheticAddress(t *testing.T) {
  function TestSourceMissingMapping (line 59) | func TestSourceMissingMapping(t *testing.T) {
  function testSourceMapping (line 66) | func testSourceMapping(t *testing.T, zeroAddress bool) {
  function TestOpenSourceFile (line 123) | func TestOpenSourceFile(t *testing.T) {
  function TestIndentation (line 218) | func TestIndentation(t *testing.T) {
  function TestRightPad (line 238) | func TestRightPad(t *testing.T) {
  function readProfile (line 260) | func readProfile(fname string, t *testing.T) *profile.Profile {

FILE: internal/report/stacks.go
  type StackSet (line 31) | type StackSet struct
    method makeInitialStacks (line 106) | func (s *StackSet) makeInitialStacks(rpt *Report) {
    method fillPlaces (line 186) | func (s *StackSet) fillPlaces() {
    method Legend (line 208) | func (s *StackSet) Legend() []string {
  type Stack (line 42) | type Stack struct
  type StackSource (line 48) | type StackSource struct
  type StackSlot (line 77) | type StackSlot struct
  method Stacks (line 83) | func (rpt *Report) Stacks() StackSet {
  function pickColor (line 200) | func pickColor(key string) int {
  function addLineInfo (line 212) | func addLineInfo(str string, line profile.Line) string {

FILE: internal/report/stacks_test.go
  function makeTestStacks (line 14) | func makeTestStacks(samples ...*profile.Sample) StackSet {
  function TestStacks (line 20) | func TestStacks(t *testing.T) {
  function TestStackSources (line 98) | func TestStackSources(t *testing.T) {
  function TestLegend (line 187) | func TestLegend(t *testing.T) {
  function findSource (line 203) | func findSource(stacks StackSet, name string) StackSource {
  function clearLineAndColumn (line 214) | func clearLineAndColumn(locs []*profile.Location) []*profile.Location {
  function makeFileLocation (line 229) | func makeFileLocation(loc *profile.Location) *profile.Location {

FILE: internal/report/synth.go
  type synthCode (line 8) | type synthCode struct
    method address (line 25) | func (s *synthCode) address(loc *profile.Location) uint64 {
  function newSynthCode (line 13) | func newSynthCode(mappings []*profile.Mapping) *synthCode {

FILE: internal/report/synth_test.go
  function TestSynthAddresses (line 9) | func TestSynthAddresses(t *testing.T) {
  function TestSynthAvoidsMapping (line 25) | func TestSynthAvoidsMapping(t *testing.T) {

FILE: internal/report/testdata/sample/sample.go
  function main (line 30) | func main() {
  function busyLoop (line 43) | func busyLoop() {

FILE: internal/symbolizer/symbolizer.go
  type Symbolizer (line 36) | type Symbolizer struct
    method Symbolize (line 50) | func (s *Symbolizer) Symbolize(mode string, sources plugin.MappingSour...
  function postURL (line 101) | func postURL(source, post string, tr http.RoundTripper) ([]byte, error) {
  function statusCodeError (line 116) | func statusCodeError(resp *http.Response) error {
  function doLocalSymbolize (line 129) | func doLocalSymbolize(prof *profile.Profile, fast, force bool, obj plugi...
  function symbolizeOneMapping (line 207) | func symbolizeOneMapping(m *profile.Mapping, locs []*profile.Location, o...
  function Demangle (line 249) | func Demangle(prof *profile.Profile, force bool, demanglerMode string) {
  function demanglerModeToOptions (line 269) | func demanglerModeToOptions(demanglerMode string) []demangle.Option {
  function demangleSingleFunction (line 284) | func demangleSingleFunction(fn *profile.Function, opts []demangle.Option) {
  function looksLikeDemangledCPlusPlus (line 321) | func looksLikeDemangledCPlusPlus(demangled string) bool {
  function removeMatching (line 334) | func removeMatching(name string, start, end byte) string {

FILE: internal/symbolizer/symbolizer_test.go
  constant filePath (line 29) | filePath = "mapping"
  constant buildID (line 30) | buildID = "build-id"
  function TestSymbolization (line 103) | func TestSymbolization(t *testing.T) {
  function symbolzMock (line 171) | func symbolzMock(p *profile.Profile, force bool, sources plugin.MappingS...
  function localMock (line 180) | func localMock(p *profile.Profile, fast, force bool, obj plugin.ObjTool,...
  function demangleMock (line 192) | func demangleMock(p *profile.Profile, force bool, mode string) {
  function TestLocalSymbolization (line 201) | func TestLocalSymbolization(t *testing.T) {
  function TestLocalSymbolizationHandlesSpecialCases (line 229) | func TestLocalSymbolizationHandlesSpecialCases(t *testing.T) {
  function checkSymbolizedLocation (line 292) | func checkSymbolizedLocation(a uint64, got []profile.Line) error {
  function frame (line 327) | func frame(fname, file string, line int, column int) plugin.Frame {
  function TestDemangleSingleFunction (line 335) | func TestDemangleSingleFunction(t *testing.T) {
  type mockObjTool (line 457) | type mockObjTool struct
    method Open (line 459) | func (mockObjTool) Open(file string, start, limit, offset uint64, relo...
    method Disasm (line 466) | func (mockObjTool) Disasm(file string, start, end uint64, intelSyntax ...
  type mockObjFile (line 473) | type mockObjFile struct
    method Name (line 477) | func (mockObjFile) Name() string {
    method ObjAddr (line 481) | func (mockObjFile) ObjAddr(addr uint64) (uint64, error) {
    method BuildID (line 485) | func (mockObjFile) BuildID() string {
    method SourceLine (line 489) | func (mf mockObjFile) SourceLine(addr uint64) ([]plugin.Frame, error) {
    method Symbols (line 493) | func (mockObjFile) Symbols(r *regexp.Regexp, addr uint64) ([]*plugin.S...
    method Close (line 497) | func (mockObjFile) Close() error {

FILE: internal/symbolz/symbolz.go
  function Symbolize (line 43) | func Symbolize(p *profile.Profile, force bool, sources plugin.MappingSou...
  function hasGperftoolsSuffix (line 69) | func hasGperftoolsSuffix(path string) bool {
  function symbolz (line 86) | func symbolz(source string) string {
  function symbolizeMapping (line 104) | func symbolizeMapping(source string, offset int64, syms func(string, str...
  function adjust (line 184) | func adjust(addr uint64, offset int64) (uint64, bool) {

FILE: internal/symbolz/symbolz_test.go
  function TestSymbolzURL (line 28) | func TestSymbolzURL(t *testing.T) {
  function TestSymbolize (line 57) | func TestSymbolize(t *testing.T) {
  function testProfile (line 93) | func testProfile(hasFunctions bool) *profile.Profile {
  function checkSymbolized (line 116) | func checkSymbolized(locs []*profile.Location, wantSymbolized bool) error {
  function fetchSymbols (line 134) | func fetchSymbols(source, post string) ([]byte, error) {
  function TestAdjust (line 145) | func TestAdjust(t *testing.T) {

FILE: internal/transport/transport.go
  type transport (line 30) | type transport struct
    method initialize (line 63) | func (tr *transport) initialize() error {
    method RoundTrip (line 102) | func (tr *transport) RoundTrip(req *http.Request) (*http.Response, err...
  constant extraUsage (line 40) | extraUsage = `    -tls_cert             TLS client certificate file for ...
  function New (line 49) | func New(flagset plugin.FlagSet) http.RoundTripper {

FILE: pprof.go
  function main (line 29) | func main() {
  type readlineUI (line 41) | type readlineUI struct
    method ReadLine (line 58) | func (r *readlineUI) ReadLine(prompt string) (string, error) {
    method Print (line 65) | func (r *readlineUI) Print(args ...interface{}) {
    method PrintErr (line 75) | func (r *readlineUI) PrintErr(args ...interface{}) {
    method IsTerminal (line 96) | func (r *readlineUI) IsTerminal() bool {
    method WantBrowser (line 101) | func (r *readlineUI) WantBrowser() bool {
    method SetAutoComplete (line 107) | func (r *readlineUI) SetAutoComplete(complete func(string) string) {
  function newUI (line 45) | func newUI() driver.UI {
  function colorize (line 87) | func colorize(msg string) string {

FILE: profile/encode.go
  method decoder (line 23) | func (p *Profile) decoder() []decoder {
  method preEncode (line 30) | func (p *Profile) preEncode() {
  method encode (line 133) | func (p *Profile) encode(b *buffer) {
  method postDecode (line 249) | func (p *Profile) postDecode() error {
  function padStringArray (line 398) | func padStringArray(arr []string, l int) []string {
  method decoder (line 405) | func (p *ValueType) decoder() []decoder {
  method encode (line 409) | func (p *ValueType) encode(b *buffer) {
  method decoder (line 422) | func (p *Sample) decoder() []decoder {
  method encode (line 426) | func (p *Sample) encode(b *buffer) {
  method decoder (line 449) | func (p label) decoder() []decoder {
  method encode (line 453) | func (p label) encode(b *buffer) {
  method decoder (line 472) | func (p *Mapping) decoder() []decoder {
  method encode (line 476) | func (p *Mapping) encode(b *buffer) {
  method decoder (line 503) | func (p *Location) decoder() []decoder {
  method encode (line 507) | func (p *Location) encode(b *buffer) {
  method decoder (line 531) | func (p *Line) decoder() []decoder {
  method encode (line 535) | func (p *Line) encode(b *buffer) {
  method decoder (line 551) | func (p *Function) decoder() []decoder {
  method encode (line 555) | func (p *Function) encode(b *buffer) {
  function addString (line 577) | func addString(strings map[string]int, s string) int64 {
  function getString (line 586) | func getString(strings []string, strng *int64, err error) (string, error) {

FILE: profile/filter.go
  method FilterSamplesByName (line 24) | func (p *Profile) FilterSamplesByName(focus, ignore, hide, show *regexp....
  method ShowFrom (line 91) | func (p *Profile) ShowFrom(showFrom *regexp.Regexp) (matched bool) {
  function filterShowFromLocation (line 122) | func filterShowFromLocation(loc *Location, showFrom *regexp.Regexp) bool {
  method lastMatchedLineIndex (line 135) | func (loc *Location) lastMatchedLineIndex(re *regexp.Regexp) int {
  method FilterTagsByName (line 148) | func (p *Profile) FilterTagsByName(show, hide *regexp.Regexp) (sm, hm bo...
  method matchesName (line 179) | func (loc *Location) matchesName(re *regexp.Regexp) bool {
  method unmatchedLines (line 195) | func (loc *Location) unmatchedLines(re *regexp.Regexp) []Line {
  method matchedLines (line 213) | func (loc *Location) matchedLines(re *regexp.Regexp) []Line {
  function focusedAndNotIgnored (line 233) | func focusedAndNotIgnored(locs []*Location, m map[uint64]bool) bool {
  type TagMatch (line 251) | type TagMatch
  method FilterSamplesByTag (line 256) | func (p *Profile) FilterSamplesByTag(focus, ignore TagMatch) (fm, im boo...

FILE: profile/filter_test.go
  function TestFilterSamplesByName (line 126) | func TestFilterSamplesByName(t *testing.T) {
  function TestShowFrom (line 417) | func TestShowFrom(t *testing.T) {
  function sampleFuncs (line 545) | func sampleFuncs(p *Profile) []string {
  function TestTagFilter (line 559) | func TestTagFilter(t *testing.T) {

FILE: profile/index.go
  method SampleIndexByName (line 26) | func (p *Profile) SampleIndexByName(sampleIndex string) (int, error) {
  function sampleTypes (line 58) | func sampleTypes(p *Profile) []string {

FILE: profile/index_test.go
  function TestSampleIndexByName (line 21) | func TestSampleIndexByName(t *testing.T) {

FILE: profile/legacy_java_profile.go
  function javaCPUProfile (line 42) | func javaCPUProfile(b []byte, period int64, parse func(b []byte) (uint64...
  function parseJavaProfile (line 68) | func parseJavaProfile(b []byte) (*Profile, error) {
  function parseJavaHeader (line 112) | func parseJavaHeader(pType string, b []byte, p *Profile) ([]byte, error) {
  function parseJavaSamples (line 167) | func parseJavaSamples(pType string, b []byte, p *Profile) ([]byte, map[u...
  function parseJavaLocations (line 239) | func parseJavaLocations(b []byte, locs map[uint64]*Location, p *Profile)...

FILE: profile/legacy_profile.go
  function isSpaceOrComment (line 72) | func isSpaceOrComment(line string) bool {
  function parseGoCount (line 79) | func parseGoCount(b []byte) (*Profile, error) {
  method remapLocationIDs (line 151) | func (p *Profile) remapLocationIDs() {
  method remapFunctionIDs (line 168) | func (p *Profile) remapFunctionIDs() {
  method remapMappingIDs (line 190) | func (p *Profile) remapMappingIDs() {
  function get32l (line 265) | func get32l(b []byte) (uint64, []byte) {
  function get32b (line 272) | func get32b(b []byte) (uint64, []byte) {
  function get64l (line 279) | func get64l(b []byte) (uint64, []byte) {
  function get64b (line 286) | func get64b(b []byte) (uint64, []byte) {
  function parseCPU (line 304) | func parseCPU(b []byte) (*Profile, error) {
  function cpuProfile (line 331) | func cpuProfile(b []byte, period int64, parse func(b []byte) (uint64, []...
  function cleanupDuplicateLocations (line 389) | func cleanupDuplicateLocations(p *Profile) {
  function parseCPUSamples (line 423) | func parseCPUSamples(b []byte, parse func(b []byte) (uint64, []byte), ad...
  function parseHeap (line 468) | func parseHeap(b []byte) (p *Profile, err error) {
  function parseHeapHeader (line 560) | func parseHeapHeader(line string) (sampling string, period int64, hasAll...
  function parseHeapSample (line 589) | func parseHeapSample(line string, rate int64, sampling string, includeAl...
  function parseHexAddresses (line 641) | func parseHexAddresses(s string) ([]uint64, error) {
  function scaleHeapSample (line 663) | func scaleHeapSample(count, size, rate int64) (int64, int64) {
  function parseContention (line 683) | func parseContention(b []byte) (*Profile, error) {
  function parseContentionSample (line 804) | func parseContentionSample(line string, period, cpuHz int64) (value []in...
  function parseThread (line 840) | func parseThread(b []byte) (*Profile, error) {
  function parseThreadSample (line 925) | func parseThreadSample(s *bufio.Scanner) (nextl string, addrs []uint64, ...
  function parseAdditionalSections (line 959) | func parseAdditionalSections(s *bufio.Scanner, p *Profile) error {
  function ParseProcMaps (line 972) | func ParseProcMaps(rd io.Reader) ([]*Mapping, error) {
  function parseProcMapsFromScanner (line 977) | func parseProcMapsFromScanner(s *bufio.Scanner) ([]*Mapping, error) {
  function removeLoggingInfo (line 1013) | func removeLoggingInfo(line string) string {
  method ParseMemoryMap (line 1023) | func (p *Profile) ParseMemoryMap(rd io.Reader) error {
  method ParseMemoryMapFromScanner (line 1031) | func (p *Profile) ParseMemoryMapFromScanner(s *bufio.Scanner) error {
  function parseMappingEntry (line 1044) | func parseMappingEntry(l string) (*Mapping, error) {
  function isMemoryMapSentinel (line 1084) | func isMemoryMapSentinel(line string) bool {
  method addLegacyFrameInfo (line 1093) | func (p *Profile) addLegacyFrameInfo() {
  function isProfileType (line 1115) | func isProfileType(p *Profile, types [][]string) bool {

FILE: profile/legacy_profile_test.go
  function TestLegacyProfileType (line 26) | func TestLegacyProfileType(t *testing.T) {
  function TestCpuParse (line 61) | func TestCpuParse(t *testing.T) {
  function parseString (line 86) | func parseString(b []byte) (uint64, []byte) {
  function checkTestSample (line 99) | func checkTestSample(p *Profile, want []uint64) error {
  function profileOfType (line 114) | func profileOfType(sampleTypes []string) *Profile {
  function TestParseMappingEntry (line 124) | func TestParseMappingEntry(t *testing.T) {
  function TestParseThreadProfileWithInvalidAddress (line 270) | func TestParseThreadProfileWithInvalidAddress(t *testing.T) {
  function TestParseGoCount (line 285) | func TestParseGoCount(t *testing.T) {

FILE: profile/merge.go
  method Compact (line 29) | func (p *Profile) Compact() *Profile {
  function Merge (line 44) | func Merge(srcs []*Profile) (*Profile, error) {
  method Normalize (line 94) | func (p *Profile) Normalize(pb *Profile) error {
  function isZeroSample (line 126) | func isZeroSample(s *Sample) bool {
  type profileMerger (line 135) | type profileMerger struct
    method mapSample (line 155) | func (pm *profileMerger) mapSample(src *Sample) *Sample {
    method sampleKey (line 196) | func (pm *profileMerger) sampleKey(sample *Sample) sampleKey {
    method mapLocation (line 284) | func (pm *profileMerger) mapLocation(src *Location) *Location {
    method mapMapping (line 346) | func (pm *profileMerger) mapMapping(src *Mapping) mapInfo {
    method mapLine (line 417) | func (pm *profileMerger) mapLine(src Line) Line {
    method mapFunction (line 426) | func (pm *profileMerger) mapFunction(src *Function) *Function {
  type mapInfo (line 150) | type mapInfo struct
  type sampleKey (line 248) | type sampleKey
  function sortedKeys1 (line 255) | func sortedKeys1(m map[string][]string) []string {
  function sortedKeys2 (line 272) | func sortedKeys2(m map[string][]int64) []string {
  method key (line 318) | func (l *Location) key() locationKey {
  type locationKey (line 340) | type locationKey struct
  method key (line 386) | func (m *Mapping) key() mappingKey {
  type mappingKey (line 412) | type mappingKey struct
  method key (line 452) | func (f *Function) key() functionKey {
  type functionKey (line 461) | type functionKey struct
  function combineHeaders (line 468) | func combineHeaders(srcs []*Profile) (*Profile, error) {
  method compatible (line 524) | func (p *Profile) compatible(pb *Profile) error {
  function equalValueType (line 543) | func equalValueType(st1, st2 *ValueType) bool {
  type locationIDMap (line 549) | type locationIDMap struct
    method get (line 561) | func (lm locationIDMap) get(id uint64) *Location {
    method set (line 568) | func (lm locationIDMap) set(id uint64, loc *Location) {
  function makeLocationIDMap (line 554) | func makeLocationIDMap(n int) locationIDMap {
  function CompatibilizeSampleTypes (line 586) | func CompatibilizeSampleTypes(ps []*Profile) error {
  function commonSampleTypes (line 601) | func commonSampleTypes(ps []*Profile) []string {
  function compatibilizeSampleTypes (line 627) | func compatibilizeSampleTypes(p *Profile, sTypes []string) error {
  function searchValueType (line 666) | func searchValueType(vts []*ValueType, s string) int {

FILE: profile/merge_test.go
  function TestMapMapping (line 26) | func TestMapMapping(t *testing.T) {
  function TestLocationIDMap (line 174) | func TestLocationIDMap(t *testing.T) {
  function BenchmarkMerge (line 204) | func BenchmarkMerge(b *testing.B) {
  function TestCompatibilizeSampleTypes (line 227) | func TestCompatibilizeSampleTypes(t *testing.T) {
  function TestDocURLMerge (line 448) | func TestDocURLMerge(t *testing.T) {

FILE: profile/profile.go
  type Profile (line 35) | type Profile struct
    method massageMappings (line 257) | func (p *Profile) massageMappings() {
    method updateLocationMapping (line 328) | func (p *Profile) updateLocationMapping(from, to *Mapping) {
    method Write (line 345) | func (p *Profile) Write(w io.Writer) error {
    method WriteUncompressed (line 353) | func (p *Profile) WriteUncompressed(w io.Writer) error {
    method CheckValid (line 362) | func (p *Profile) CheckValid() error {
    method Aggregate (line 443) | func (p *Profile) Aggregate(inlineFrame, function, filename, linenumbe...
    method NumLabelUnits (line 499) | func (p *Profile) NumLabelUnits() (map[string]string, map[string][]str...
    method String (line 556) | func (p *Profile) String() string {
    method SetLabel (line 718) | func (p *Profile) SetLabel(key string, value []string) {
    method RemoveLabel (line 730) | func (p *Profile) RemoveLabel(key string) {
    method SetNumLabel (line 746) | func (p *Profile) SetNumLabel(key string, value []int64, unit []string) {
    method RemoveNumLabel (line 763) | func (p *Profile) RemoveNumLabel(key string) {
    method Scale (line 778) | func (p *Profile) Scale(ratio float64) {
    method ScaleN (line 791) | func (p *Profile) ScaleN(ratios []float64) error {
    method HasFunctions (line 826) | func (p *Profile) HasFunctions() bool {
    method HasFileLines (line 837) | func (p *Profile) HasFileLines() bool {
    method Copy (line 865) | func (p *Profile) Copy() *Profile {
  type ValueType (line 66) | type ValueType struct
  type Sample (line 75) | type Sample struct
    method string (line 662) | func (s *Sample) string() string {
    method HasLabel (line 737) | func (s *Sample) HasLabel(key, value string) bool {
    method DiffBaseSample (line 772) | func (s *Sample) DiffBaseSample() bool {
  type label (line 101) | type label struct
  type Mapping (line 111) | type Mapping struct
    method string (line 604) | func (m *Mapping) string() string {
    method Unsymbolizable (line 849) | func (m *Mapping) Unsymbolizable() bool {
  type Location (line 137) | type Location struct
    method string (line 628) | func (l *Location) string() string {
  type Line (line 148) | type Line struct
  type Function (line 157) | type Function struct
  function Parse (line 172) | func Parse(r io.Reader) (*Profile, error) {
  function ParseData (line 182) | func ParseData(data []byte) (*Profile, error) {
  function parseLegacy (line 213) | func parseLegacy(data []byte) (*Profile, error) {
  function ParseUncompressed (line 237) | func ParseUncompressed(data []byte) (*Profile, error) {
  function adjacent (line 305) | func adjacent(m1, m2 *Mapping) bool {
  function serialize (line 336) | func serialize(p *Profile) []byte {
  function labelsToString (line 685) | func labelsToString(labels map[string][]string) string {
  function numLabelsToString (line 696) | func numLabelsToString(numLabels map[string][]int64, numUnits map[string...

FILE: profile/profile_test.go
  function TestParse (line 34) | func TestParse(t *testing.T) {
  function TestParseError (line 104) | func TestParseError(t *testing.T) {
  function TestParseConcatentated (line 120) | func TestParseConcatentated(t *testing.T) {
  function TestCheckValid (line 135) | func TestCheckValid(t *testing.T) {
  function leaveTempfile (line 199) | func leaveTempfile(b []byte) string {
  constant mainBinary (line 210) | mainBinary = "/bin/main"
  type aggTest (line 648) | type aggTest struct
  constant totalSamples (line 654) | totalSamples = int64(11112)
  function TestAggregation (line 656) | func TestAggregation(t *testing.T) {
  function checkAggregation (line 673) | func checkAggregation(prof *Profile, a *aggTest) error {
  function TestScale (line 735) | func TestScale(t *testing.T) {
  function TestMergeMain (line 802) | func TestMergeMain(t *testing.T) {
  function TestMerge (line 813) | func TestMerge(t *testing.T) {
  function TestMergeAll (line 850) | func TestMergeAll(t *testing.T) {
  function TestIsFoldedMerge (line 873) | func TestIsFoldedMerge(t *testing.T) {
  function TestNumLabelMerge (line 906) | func TestNumLabelMerge(t *testing.T) {
  function TestEmptyMappingMerge (line 963) | func TestEmptyMappingMerge(t *testing.T) {
  function TestNormalizeBySameProfile (line 997) | func TestNormalizeBySameProfile(t *testing.T) {
  function TestNormalizeByDifferentProfile (line 1015) | func TestNormalizeByDifferentProfile(t *testing.T) {
  function TestNormalizeByMultipleOfSameProfile (line 1039) | func TestNormalizeByMultipleOfSameProfile(t *testing.T) {
  function TestNormalizeIncompatibleProfiles (line 1064) | func TestNormalizeIncompatibleProfiles(t *testing.T) {
  function locationHash (line 1074) | func locationHash(s *Sample) string {
  function TestHasLabel (line 1084) | func TestHasLabel(t *testing.T) {
  function TestDiffBaseSample (line 1149) | func TestDiffBaseSample(t *testing.T) {
  function TestRemove (line 1202) | func TestRemove(t *testing.T) {
  function TestSetLabel (line 1269) | func TestSetLabel(t *testing.T) {
  function TestSetNumLabel (line 1402) | func TestSetNumLabel(t *testing.T) {
  function TestRemoveNumLabel (line 1594) | func TestRemoveNumLabel(t *testing.T) {
  function TestNumLabelUnits (line 1741) | func TestNumLabelUnits(t *testing.T) {
  function TestSetMain (line 1858) | func TestSetMain(t *testing.T) {
  function TestParseKernelRelocation (line 1865) | func TestParseKernelRelocation(t *testing.T) {
  function TestEncodeDecodeDocURL (line 1872) | func TestEncodeDecodeDocURL(t *testing.T) {
  function parallel (line 1895) | func parallel(n int, fn func()) {
  function TestThreadSafety (line 1907) | func TestThreadSafety(t *testing.T) {
  function BenchmarkParse (line 1920) | func BenchmarkParse(b *testing.B) {
  function BenchmarkWrite (line 1931) | func BenchmarkWrite(b *testing.B) {
  function TestMappingUnsymbolizable (line 1944) | func TestMappingUnsymbolizable(t *testing.T) {

FILE: profile/proto.go
  type buffer (line 42) | type buffer struct
  type decoder (line 51) | type decoder
  type message (line 53) | type message interface
  function marshal (line 58) | func marshal(m message) []byte {
  function encodeVarint (line 64) | func encodeVarint(b *buffer, x uint64) {
  function encodeLength (line 72) | func encodeLength(b *buffer, tag int, len int) {
  function encodeUint64 (line 77) | func encodeUint64(b *buffer, tag int, x uint64) {
  function encodeUint64s (line 83) | func encodeUint64s(b *buffer, tag int, x []uint64) {
  function encodeUint64Opt (line 103) | func encodeUint64Opt(b *buffer, tag int, x uint64) {
  function encodeInt64 (line 110) | func encodeInt64(b *buffer, tag int, x int64) {
  function encodeInt64s (line 115) | func encodeInt64s(b *buffer, tag int, x []int64) {
  function encodeInt64Opt (line 135) | func encodeInt64Opt(b *buffer, tag int, x int64) {
  function encodeString (line 142) | func encodeString(b *buffer, tag int, x string) {
  function encodeStrings (line 147) | func encodeStrings(b *buffer, tag int, x []string) {
  function encodeBool (line 153) | func encodeBool(b *buffer, tag int, x bool) {
  function encodeBoolOpt (line 161) | func encodeBoolOpt(b *buffer, tag int, x bool) {
  function encodeMessage (line 167) | func encodeMessage(b *buffer, tag int, m message) {
  function unmarshal (line 178) | func unmarshal(data []byte, m message) (err error) {
  function le64 (line 183) | func le64(p []byte) uint64 {
  function le32 (line 187) | func le32(p []byte) uint32 {
  function peekNumVarints (line 191) | func peekNumVarints(data []byte) (numVarints int) {
  function decodeVarint (line 201) | func decodeVarint(data []byte) (uint64, []byte, error) {
  function decodeField (line 214) | func decodeField(b *buffer, data []byte) ([]byte, error) {
  function checkType (line 259) | func checkType(b *buffer, typ int) error {
  function decodeMessage (line 266) | func decodeMessage(b *buffer, m message) error {
  function decodeInt64 (line 289) | func decodeInt64(b *buffer, x *int64) error {
  function decodeInt64s (line 297) | func decodeInt64s(b *buffer, x *[]int64) error {
  function decodeUint64 (line 323) | func decodeUint64(b *buffer, x *uint64) error {
  function decodeUint64s (line 331) | func decodeUint64s(b *buffer, x *[]uint64) error {
  function decodeString (line 357) | func decodeString(b *buffer, x *string) error {
  function decodeStrings (line 365) | func decodeStrings(b *buffer, x *[]string) error {
  function decodeBool (line 374) | func decodeBool(b *buffer, x *bool) error {

FILE: profile/proto_test.go
  function TestMarshalUnmarshal (line 162) | func TestMarshalUnmarshal(t *testing.T) {

FILE: profile/prune.go
  function simplifyFunc (line 38) | func simplifyFunc(f string) string {
  method Prune (line 56) | func (p *Profile) Prune(dropRx, keepRx *regexp.Regexp) {
  method RemoveUninteresting (line 132) | func (p *Profile) RemoveUninteresting() error {
  method PruneFrom (line 162) | func (p *Profile) PruneFrom(dropRx *regexp.Regexp) {

FILE: profile/prune_test.go
  function TestPrune (line 22) | func TestPrune(t *testing.T) {
  constant out1 (line 127) | out1 = `PeriodType: cpu milliseconds
  constant out2 (line 214) | out2 = `PeriodType: cpu milliseconds

FILE: third_party/svgpan/svgpan.js
  function setupHandlers (line 68) | function setupHandlers(root){
  function getRoot (line 85) | function getRoot(root) {
  function getEventPoint (line 108) | function getEventPoint(evt) {
  function setCTM (line 120) | function setCTM(element, matrix) {
  function dumpMatrix (line 129) | function dumpMatrix(matrix) {
  function setAttributes (line 138) | function setAttributes(element, attributes){
  function handleMouseWheel (line 146) | function handleMouseWheel(evt) {
  function handleMouseMove (line 186) | function handleMouseMove(evt) {
  function handleMouseDown (line 214) | function handleMouseDown(evt) {
  function handleMouseUp (line 249) | function handleMouseUp(evt) {
Condensed preview — 239 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,303K chars).
[
  {
    "path": ".gitattributes",
    "chars": 19,
    "preview": "* text=auto eol=lf\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "chars": 512,
    "preview": "Please answer these questions before submitting your issue. Thanks!\n\n### What version of pprof are you using?\n\nIf you ar"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 119,
    "preview": "version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"monthly\"\n"
  },
  {
    "path": ".github/workflows/ci.yaml",
    "chars": 9215,
    "preview": "name: ci\non:\n  push:\n    branches:\n      - main\n  pull_request:\n  schedule:\n    - cron: '0 2 * * *' # Run every day, at "
  },
  {
    "path": ".gitignore",
    "chars": 57,
    "preview": ".DS_Store\n*~\n*.orig\n*.exe\n.*.swp\ncore\ncoverage.txt\npprof\n"
  },
  {
    "path": "AUTHORS",
    "chars": 305,
    "preview": "# This is the official list of pprof authors for copyright purposes.\n# This file is distinct from the CONTRIBUTORS files"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 3206,
    "preview": "Want to contribute? Great: read the page (including the small print at the end).\n\n# Before you contribute\n\nAs an individ"
  },
  {
    "path": "CONTRIBUTORS",
    "chars": 654,
    "preview": "# People who have agreed to one of the CLAs and can contribute patches.\n# The AUTHORS file lists the copyright holders; "
  },
  {
    "path": "LICENSE",
    "chars": 11358,
    "preview": "\n                                 Apache License\n                           Version 2.0, January 2004\n                  "
  },
  {
    "path": "README.md",
    "chars": 4230,
    "preview": "[![Github Action CI](https://github.com/google/pprof/workflows/ci/badge.svg)](https://github.com/google/pprof/actions)\n["
  },
  {
    "path": "browsertests/README.md",
    "chars": 253,
    "preview": "Browser tests are separated out into a module of their own to avoid\npolluting pprof dependencies with chromedp.\n\nThese t"
  },
  {
    "path": "browsertests/browser_test.go",
    "chars": 5827,
    "preview": "// Copyright 2023 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "browsertests/go.mod",
    "chars": 722,
    "preview": "module github.com/google/pprof/browsertests\n\ngo 1.24.0\n\ntoolchain go1.24.9\n\n// Use the version of pprof in this director"
  },
  {
    "path": "browsertests/go.sum",
    "chars": 2248,
    "preview": "github.com/chromedp/cdproto v0.0.0-20250403032234-65de8f5d025b h1:jJmiCljLNTaq/O1ju9Bzz2MPpFlmiTn0F7LwCoeDZVw=\ngithub.co"
  },
  {
    "path": "browsertests/testdata/testfixture.js",
    "chars": 820,
    "preview": "// TestFixture records log messages and errors in an array that will\n// be returned to Go code. Each element in the resu"
  },
  {
    "path": "browsertests/testdata/testflame.js",
    "chars": 3921,
    "preview": "function TestFlame() {\n  const PADDING = 2; // Matches PADDING in stackViewer\n\n  const test = new TestFixture();\n  const"
  },
  {
    "path": "browsertests/testutils.go",
    "chars": 6090,
    "preview": "package browsertests\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"regexp\"\n\t\"runtime\"\n\t\"testing\"\n\t\"time\"\n\n\t\""
  },
  {
    "path": "doc/README.md",
    "chars": 26198,
    "preview": "# pprof\n\npprof is a tool for visualization and analysis of profiling data.\n\npprof reads a collection of profiling sample"
  },
  {
    "path": "driver/driver.go",
    "chars": 10057,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "fuzz/README.md",
    "chars": 1128,
    "preview": "This is an explanation of how to do fuzzing of ParseData. This uses github.com/dvyukov/go-fuzz/ for fuzzing.\n\n# How to u"
  },
  {
    "path": "fuzz/corpus/empty",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "fuzz/fuzz_test.go",
    "chars": 1167,
    "preview": "// Copyright 2017 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "fuzz/main.go",
    "chars": 941,
    "preview": "// Copyright 2017 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "fuzz/testdata/7e3c92482f6f39fc502b822ded792c589849cca8",
    "chars": 24,
    "preview": "--- heapz 1 ---\n0 0 @ 0\n"
  },
  {
    "path": "go.mod",
    "chars": 227,
    "preview": "module github.com/google/pprof\n\ngo 1.24.0\n\ntoolchain go1.24.9\n\nrequire (\n\tgithub.com/chzyer/readline v1.5.1\n\tgithub.com/"
  },
  {
    "path": "go.sum",
    "chars": 1002,
    "preview": "github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=\ngithub.com/chzyer/logex v1.2.1/go.mod h1:"
  },
  {
    "path": "internal/binutils/addr2liner.go",
    "chars": 6017,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/binutils/addr2liner_llvm.go",
    "chars": 4913,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/binutils/addr2liner_nm.go",
    "chars": 4086,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/binutils/binutils.go",
    "chars": 22872,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/binutils/binutils_test.go",
    "chars": 31850,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/binutils/disasm.go",
    "chars": 5331,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/binutils/disasm_test.go",
    "chars": 4632,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/binutils/testdata/build_binaries.go",
    "chars": 2844,
    "preview": "// Copyright 2019 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/binutils/testdata/exe_mac_64.dSYM/Contents/Info.plist",
    "chars": 669,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.c"
  },
  {
    "path": "internal/binutils/testdata/fake-llvm-symbolizer",
    "chars": 1355,
    "preview": "#!/bin/sh\n#\n# Copyright 2014 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"L"
  },
  {
    "path": "internal/binutils/testdata/hello.c",
    "chars": 76,
    "preview": "#include <stdio.h>\n\nint main() {\n  printf(\"Hello, world!\\n\");\n  return 0;\n}\n"
  },
  {
    "path": "internal/binutils/testdata/lib.c",
    "chars": 53,
    "preview": "int foo() {\n  return 1;\n}\n\nint bar() {\n  return 2;\n}\n"
  },
  {
    "path": "internal/binutils/testdata/lib_mac_64.dSYM/Contents/Info.plist",
    "chars": 669,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.c"
  },
  {
    "path": "internal/binutils/testdata/malformed_elf",
    "chars": 4,
    "preview": "ELF"
  },
  {
    "path": "internal/binutils/testdata/malformed_macho",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "internal/driver/cli.go",
    "chars": 12700,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/driver/commands.go",
    "chars": 18897,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/driver/config.go",
    "chars": 10720,
    "preview": "package driver\n\nimport (\n\t\"fmt\"\n\t\"net/url\"\n\t\"reflect\"\n\t\"slices\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n)\n\n// config holds setting"
  },
  {
    "path": "internal/driver/driver.go",
    "chars": 11083,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/driver/driver_focus.go",
    "chars": 6568,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/driver/driver_test.go",
    "chars": 49112,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/driver/fetch.go",
    "chars": 19439,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/driver/fetch_test.go",
    "chars": 23068,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/driver/flags.go",
    "chars": 1953,
    "preview": "//  Copyright 2018 Google Inc. All Rights Reserved.\n//\n//  Licensed under the Apache License, Version 2.0 (the \"License\""
  },
  {
    "path": "internal/driver/html/common.css",
    "chars": 5892,
    "preview": "* {\n  margin: 0;\n  padding: 0;\n  box-sizing: border-box;\n}\nhtml, body {\n  height: 100%;\n}\nbody {\n  font-family: 'Roboto'"
  },
  {
    "path": "internal/driver/html/common.js",
    "chars": 20483,
    "preview": "// Make svg pannable and zoomable.\n// Call clickHandler(t) if a click event is caught by the pan event handlers.\nfunctio"
  },
  {
    "path": "internal/driver/html/graph.css",
    "chars": 70,
    "preview": "#graph {\n    cursor: grab;\n}\n\n#graph:active {\n    cursor: grabbing;\n}\n"
  },
  {
    "path": "internal/driver/html/graph.html",
    "chars": 334,
    "preview": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n  <title>{{.Title}}</title>\n  {{template \"css\" .}}\n  {{template \""
  },
  {
    "path": "internal/driver/html/header.html",
    "chars": 3686,
    "preview": "<div class=\"header\">\n  <div class=\"title\">\n    <h1><a href=\"./\">pprof</a></h1>\n  </div>\n\n  <div id=\"view\" class=\"menu-it"
  },
  {
    "path": "internal/driver/html/plaintext.html",
    "chars": 324,
    "preview": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n  <title>{{.Title}}</title>\n  {{template \"css\" .}}\n</head>\n<body>"
  },
  {
    "path": "internal/driver/html/source.html",
    "chars": 2508,
    "preview": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n  <title>{{.Title}}</title>\n  {{if not .Standalone}}{{template \"c"
  },
  {
    "path": "internal/driver/html/stacks.css",
    "chars": 2176,
    "preview": "body {\n  overflow: hidden; /* Want scrollbar not here, but in #stack-holder */\n}\n/* Scrollable container for flame graph"
  },
  {
    "path": "internal/driver/html/stacks.html",
    "chars": 1121,
    "preview": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n  <title>{{.Title}}</title>\n  {{template \"css\" .}}\n  {{template \""
  },
  {
    "path": "internal/driver/html/stacks.js",
    "chars": 20366,
    "preview": "// stackViewer displays a flame-graph like view (extended to show callers).\n//   stacks - report.StackSet\n//   nodes  - "
  },
  {
    "path": "internal/driver/html/top.html",
    "chars": 3196,
    "preview": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n  <title>{{.Title}}</title>\n  {{template \"css\" .}}\n  <style type="
  },
  {
    "path": "internal/driver/interactive.go",
    "chars": 10896,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/driver/interactive_test.go",
    "chars": 7814,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/driver/options.go",
    "chars": 2318,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/driver/settings.go",
    "chars": 4260,
    "preview": "package driver\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/url\"\n\t\"os\"\n\t\"path/filepath\"\n)\n\n// settings holds pprof settings.\n"
  },
  {
    "path": "internal/driver/settings_test.go",
    "chars": 6775,
    "preview": "package driver\n\nimport (\n\t\"net/url\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"testing\"\n)\n\n// settingsDirAndFile returns a dire"
  },
  {
    "path": "internal/driver/stacks.go",
    "chars": 1709,
    "preview": "// Copyright 2022 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/driver/svg.go",
    "chars": 2256,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/driver/tagroot.go",
    "chars": 3401,
    "preview": "package driver\n\nimport (\n\t\"strings\"\n\n\t\"github.com/google/pprof/internal/measurement\"\n\t\"github.com/google/pprof/profile\"\n"
  },
  {
    "path": "internal/driver/tagroot_test.go",
    "chars": 11507,
    "preview": "package driver\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/google/pprof/internal/proftest\"\n\t\"github.com/google/"
  },
  {
    "path": "internal/driver/tempfile.go",
    "chars": 1761,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/driver/tempfile_test.go",
    "chars": 1206,
    "preview": "package driver\n\nimport (\n\t\"os\"\n\t\"sync\"\n\t\"testing\"\n)\n\nfunc TestNewTempFile(t *testing.T) {\n\tconst n = 100\n\t// Line up rea"
  },
  {
    "path": "internal/driver/testdata/cppbench.contention",
    "chars": 1551,
    "preview": "--- contentionz 1 ---\ncycles/second = 3201000000\nsampling period = 100\nms since reset = 16502830\ndiscarded samples = 0\n "
  },
  {
    "path": "internal/driver/testdata/cppbench.small.contention",
    "chars": 821,
    "preview": "--- contentionz 1 ---\ncycles/second = 3201000000\nsampling period = 100\nms since reset = 16502830\ndiscarded samples = 0\n "
  },
  {
    "path": "internal/driver/testdata/file1000.src",
    "chars": 94,
    "preview": "line1\nline2\nline3\nline4\nline5\nline6\nline7\nline8\nline9\nline0\nline1\nline2\nline3\nline4\nline5\n\t\t\n\n"
  },
  {
    "path": "internal/driver/testdata/file2000.src",
    "chars": 94,
    "preview": "line1\nline2\nline3\nline4\nline5\nline6\nline7\nline8\nline9\nline0\nline1\nline2\nline3\nline4\nline5\n\t\t\n\n"
  },
  {
    "path": "internal/driver/testdata/file3000.src",
    "chars": 94,
    "preview": "line1\nline2\nline3\nline4\nline5\nline6\nline7\nline8\nline9\nline0\nline1\nline2\nline3\nline4\nline5\n\t\t\n\n"
  },
  {
    "path": "internal/driver/testdata/pprof.contention.cum.files.dot",
    "chars": 1440,
    "preview": "digraph \"unnamed\" {\nnode [style=filled fillcolor=\"#f8f8f8\"]\nsubgraph cluster_L { \"Build ID: buildid-contention\" [shape=b"
  },
  {
    "path": "internal/driver/testdata/pprof.contention.flat.addresses.dot.focus.ignore",
    "chars": 1678,
    "preview": "digraph \"unnamed\" {\nnode [style=filled fillcolor=\"#f8f8f8\"]\nsubgraph cluster_L { \"Build ID: buildid-contention\" [shape=b"
  },
  {
    "path": "internal/driver/testdata/pprof.cpu.addresses.traces",
    "chars": 1637,
    "preview": "File: testbinary\nType: cpu\nDuration: 10s, Total samples = 1.12s (11.20%)\n-----------+-----------------------------------"
  },
  {
    "path": "internal/driver/testdata/pprof.cpu.call_tree.callgrind",
    "chars": 1070,
    "preview": "positions: instr line\nevents: cpu(ms)\n\nob=(1) /path/to/testbinary\nfl=(1) testdata/file1000.src\nfn=(1) line1000\n0x1000 1 "
  },
  {
    "path": "internal/driver/testdata/pprof.cpu.callgrind",
    "chars": 831,
    "preview": "positions: instr line\nevents: cpu(ms)\n\nob=(1) /path/to/testbinary\nfl=(1) testdata/file1000.src\nfn=(1) line1000\n0x1000 1 "
  },
  {
    "path": "internal/driver/testdata/pprof.cpu.comments",
    "chars": 13,
    "preview": "some-comment\n"
  },
  {
    "path": "internal/driver/testdata/pprof.cpu.cum.lines.text.focus.hide",
    "chars": 391,
    "preview": "Active filters:\n   focus=[12]00\n   hide=line[X3]0\nShowing nodes accounting for 1.11s, 99.11% of 1.12s total\n      flat  "
  },
  {
    "path": "internal/driver/testdata/pprof.cpu.cum.lines.text.hide",
    "chars": 375,
    "preview": "Active filters:\n   hide=line[X3]0\nShowing nodes accounting for 1.11s, 99.11% of 1.12s total\n      flat  flat%   sum%    "
  },
  {
    "path": "internal/driver/testdata/pprof.cpu.cum.lines.text.show",
    "chars": 372,
    "preview": "Active filters:\n   show=[12]00\nShowing nodes accounting for 1.11s, 99.11% of 1.12s total\n      flat  flat%   sum%       "
  },
  {
    "path": "internal/driver/testdata/pprof.cpu.cum.lines.topproto.hide",
    "chars": 210,
    "preview": "Active filters:\n   hide=mangled[X3]0\nShowing nodes accounting for 1s, 100% of 1s total\n      flat  flat%   sum%        c"
  },
  {
    "path": "internal/driver/testdata/pprof.cpu.cum.lines.tree.show_from",
    "chars": 1208,
    "preview": "Active filters:\n   show_from=line2\nShowing nodes accounting for 1.01s, 90.18% of 1.12s total\n---------------------------"
  },
  {
    "path": "internal/driver/testdata/pprof.cpu.flat.addresses.disasm",
    "chars": 942,
    "preview": "Total: 1.12s\nROUTINE ======================== line1000\n     1.10s      1.10s (flat, cum) 98.21% of Total\n     1.10s     "
  },
  {
    "path": "internal/driver/testdata/pprof.cpu.flat.addresses.noinlines.text",
    "chars": 505,
    "preview": "Showing nodes accounting for 1.12s, 100% of 1.12s total\nDropped 1 node (cum <= 0.06s)\n      flat  flat%   sum%        cu"
  },
  {
    "path": "internal/driver/testdata/pprof.cpu.flat.addresses.weblist",
    "chars": 5772,
    "preview": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n  <title>testbinary cpu</title>\n  \n  <style type=\"text/css\">\nbody"
  },
  {
    "path": "internal/driver/testdata/pprof.cpu.flat.filefunctions.noinlines.text",
    "chars": 324,
    "preview": "Showing nodes accounting for 1.12s, 100% of 1.12s total\n      flat  flat%   sum%        cum   cum%\n     1.10s 98.21% 98."
  },
  {
    "path": "internal/driver/testdata/pprof.cpu.flat.functions.call_tree.dot",
    "chars": 2518,
    "preview": "digraph \"testbinary\" {\nnode [style=filled fillcolor=\"#f8f8f8\"]\nsubgraph cluster_L { \"File: testbinary\" [shape=box fontsi"
  },
  {
    "path": "internal/driver/testdata/pprof.cpu.flat.functions.dot",
    "chars": 2401,
    "preview": "digraph \"testbinary\" {\nnode [style=filled fillcolor=\"#f8f8f8\"]\nsubgraph cluster_L { \"File: testbinary\" [shape=box fontsi"
  },
  {
    "path": "internal/driver/testdata/pprof.cpu.flat.functions.noinlines.text",
    "chars": 258,
    "preview": "Showing nodes accounting for 1.12s, 100% of 1.12s total\n      flat  flat%   sum%        cum   cum%\n     1.10s 98.21% 98."
  },
  {
    "path": "internal/driver/testdata/pprof.cpu.flat.functions.text",
    "chars": 444,
    "preview": "Showing nodes accounting for 1.12s, 100% of 1.12s total\n      flat  flat%   sum%        cum   cum%\n     1.10s 98.21% 98."
  },
  {
    "path": "internal/driver/testdata/pprof.cpu.lines.topproto",
    "chars": 173,
    "preview": "Showing nodes accounting for 1s, 100% of 1s total\n      flat  flat%   sum%        cum   cum%\n        1s   100%   100%   "
  },
  {
    "path": "internal/driver/testdata/pprof.cpu.peek",
    "chars": 941,
    "preview": "Showing nodes accounting for 1.12s, 100% of 1.12s total\n----------------------------------------------------------+-----"
  },
  {
    "path": "internal/driver/testdata/pprof.cpu.tags",
    "chars": 310,
    "preview": " key1: Total 1.12s of 1.12s (  100%)\n          1s (89.29%): tag1\n       100ms ( 8.93%): tag2\n        10ms ( 0.89%): tag3"
  },
  {
    "path": "internal/driver/testdata/pprof.cpu.tags.focus.ignore",
    "chars": 132,
    "preview": " key1: Total 100ms of 1.12s ( 8.93%)\n       100ms ( 8.93%): tag2\n\n key3: Total 100ms of 1.12s ( 8.93%)\n       100ms ( 8."
  },
  {
    "path": "internal/driver/testdata/pprof.cpu.traces",
    "chars": 981,
    "preview": "File: testbinary\nType: cpu\nDuration: 10s, Total samples = 1.12s (11.20%)\n-----------+-----------------------------------"
  },
  {
    "path": "internal/driver/testdata/pprof.cpusmall.flat.addresses.tree",
    "chars": 1318,
    "preview": "Showing nodes accounting for 4s, 100% of 4s total\nShowing top 4 nodes out of 5\n-----------------------------------------"
  },
  {
    "path": "internal/driver/testdata/pprof.heap.callgrind",
    "chars": 775,
    "preview": "positions: instr line\nevents: inuse_space(MB)\n\nob=\nfl=(1) testdata/file2000.src\nfn=(1) line2001\n0x2000 2 62\ncfl=(2) test"
  },
  {
    "path": "internal/driver/testdata/pprof.heap.comments",
    "chars": 24,
    "preview": "comment\n#hidden comment\n"
  },
  {
    "path": "internal/driver/testdata/pprof.heap.cum.lines.tree.focus",
    "chars": 1615,
    "preview": "Active filters:\n   focus=[24]00\nShowing nodes accounting for 62.50MB, 63.37% of 98.63MB total\nDropped 2 nodes (cum <= 4."
  },
  {
    "path": "internal/driver/testdata/pprof.heap.cum.relative_percentages.tree.focus",
    "chars": 1375,
    "preview": "Active filters:\n   focus=[24]00\nShowing nodes accounting for 62.50MB, 98.46% of 63.48MB total\nDropped 2 nodes (cum <= 3."
  },
  {
    "path": "internal/driver/testdata/pprof.heap.flat.files.seconds.text",
    "chars": 89,
    "preview": "Showing nodes accounting for 0, 0% of 0 total\n      flat  flat%   sum%        cum   cum%\n"
  },
  {
    "path": "internal/driver/testdata/pprof.heap.flat.files.text",
    "chars": 268,
    "preview": "Showing nodes accounting for 93.75MB, 95.05% of 98.63MB total\nDropped 1 node (cum <= 4.93MB)\n      flat  flat%   sum%   "
  },
  {
    "path": "internal/driver/testdata/pprof.heap.flat.files.text.focus",
    "chars": 353,
    "preview": "Active filters:\n   focus=[12]00\n   taghide=[X3]00\nShowing nodes accounting for 67.38MB, 68.32% of 98.63MB total\n      fl"
  },
  {
    "path": "internal/driver/testdata/pprof.heap.flat.inuse_objects.text",
    "chars": 440,
    "preview": "Showing nodes accounting for 150, 100% of 150 total\n      flat  flat%   sum%        cum   cum%\n        80 53.33% 53.33% "
  },
  {
    "path": "internal/driver/testdata/pprof.heap.flat.inuse_objects.text.all_frames",
    "chars": 627,
    "preview": "Showing nodes accounting for 150, 100% of 150 total\n      flat  flat%   sum%        cum   cum%\n       120 80.00% 80.00% "
  },
  {
    "path": "internal/driver/testdata/pprof.heap.flat.inuse_space.dot.focus",
    "chars": 1542,
    "preview": "digraph \"unnamed\" {\nnode [style=filled fillcolor=\"#f8f8f8\"]\nsubgraph cluster_L { \"Build ID: buildid\" [shape=box fontsize"
  },
  {
    "path": "internal/driver/testdata/pprof.heap.flat.inuse_space.dot.focus.ignore",
    "chars": 1902,
    "preview": "digraph \"unnamed\" {\nnode [style=filled fillcolor=\"#f8f8f8\"]\nsubgraph cluster_L { \"Build ID: buildid\" [shape=box fontsize"
  },
  {
    "path": "internal/driver/testdata/pprof.heap.flat.lines.dot.focus",
    "chars": 3461,
    "preview": "digraph \"unnamed\" {\nnode [style=filled fillcolor=\"#f8f8f8\"]\nsubgraph cluster_L { \"Build ID: buildid\" [shape=box fontsize"
  },
  {
    "path": "internal/driver/testdata/pprof.heap.tags",
    "chars": 172,
    "preview": " bytes: Total 98.63MB of 98.63MB (  100%)\n        62.50MB (63.37%): 1.56MB\n        31.25MB (31.68%): 400kB\n         3.91"
  },
  {
    "path": "internal/driver/testdata/pprof.heap.tags.unit",
    "chars": 194,
    "preview": " bytes: Total 103424000B of 103424000B (  100%)\n        65536000B (63.37%): 1638400B\n        32768000B (31.68%): 409600B"
  },
  {
    "path": "internal/driver/testdata/pprof.heap_alloc.flat.alloc_objects.text",
    "chars": 440,
    "preview": "Showing nodes accounting for 150, 100% of 150 total\n      flat  flat%   sum%        cum   cum%\n        80 53.33% 53.33% "
  },
  {
    "path": "internal/driver/testdata/pprof.heap_alloc.flat.alloc_space.dot",
    "chars": 1915,
    "preview": "digraph \"unnamed\" {\nnode [style=filled fillcolor=\"#f8f8f8\"]\nsubgraph cluster_L { \"Build ID: buildid\" [shape=box fontsize"
  },
  {
    "path": "internal/driver/testdata/pprof.heap_alloc.flat.alloc_space.dot.focus",
    "chars": 2243,
    "preview": "digraph \"unnamed\" {\nnode [style=filled fillcolor=\"#f8f8f8\"]\nsubgraph cluster_L { \"Build ID: buildid\" [shape=box fontsize"
  },
  {
    "path": "internal/driver/testdata/pprof.heap_alloc.flat.alloc_space.dot.hide",
    "chars": 1179,
    "preview": "digraph \"unnamed\" {\nnode [style=filled fillcolor=\"#f8f8f8\"]\nsubgraph cluster_L { \"Build ID: buildid\" [shape=box fontsize"
  },
  {
    "path": "internal/driver/testdata/pprof.heap_request.relative_percentages.tags.focus",
    "chars": 222,
    "preview": " bytes: Total 93.75MB of 93.75MB (  100%)\n        62.50MB (66.67%): 1.56MB\n        31.25MB (33.33%): 400kB\n\n request: To"
  },
  {
    "path": "internal/driver/testdata/pprof.heap_request.tags.focus",
    "chars": 222,
    "preview": " bytes: Total 93.75MB of 98.63MB (95.05%)\n        62.50MB (63.37%): 1.56MB\n        31.25MB (31.68%): 400kB\n\n request: To"
  },
  {
    "path": "internal/driver/testdata/pprof.heap_sizetags.dot",
    "chars": 3253,
    "preview": "digraph \"unnamed\" {\nnode [style=filled fillcolor=\"#f8f8f8\"]\nsubgraph cluster_L { \"Build ID: buildid\" [shape=box fontsize"
  },
  {
    "path": "internal/driver/testdata/pprof.heap_tags.traces",
    "chars": 958,
    "preview": "Build ID: buildid\ncomment\nType: inuse_space\n-----------+-------------------------------------------------------\n      ke"
  },
  {
    "path": "internal/driver/testdata/pprof.long_name_funcs.dot",
    "chars": 1372,
    "preview": "digraph \"testbinary\" {\nnode [style=filled fillcolor=\"#f8f8f8\"]\nsubgraph cluster_L { \"File: testbinary\" [shape=box fontsi"
  },
  {
    "path": "internal/driver/testdata/pprof.long_name_funcs.text",
    "chars": 341,
    "preview": "Showing nodes accounting for 1.11s, 100% of 1.11s total\n      flat  flat%   sum%        cum   cum%\n     1.10s 99.10% 99."
  },
  {
    "path": "internal/driver/testdata/pprof.unknown.flat.functions.call_tree.text",
    "chars": 411,
    "preview": "Showing nodes accounting for 1.12s, 100% of 1.12s total\nShowing top 5 nodes out of 6\n      flat  flat%   sum%        cum"
  },
  {
    "path": "internal/driver/webhtml.go",
    "chars": 2614,
    "preview": "// Copyright 2017 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/driver/webui.go",
    "chars": 14405,
    "preview": "// Copyright 2017 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/driver/webui_test.go",
    "chars": 9043,
    "preview": "// Copyright 2017 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/elfexec/elfexec.go",
    "chars": 14249,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/elfexec/elfexec_test.go",
    "chars": 28013,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/graph/dotgraph.go",
    "chars": 15155,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/graph/dotgraph_test.go",
    "chars": 9074,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/graph/graph.go",
    "chars": 32167,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/graph/graph_test.go",
    "chars": 12521,
    "preview": "//  Copyright 2016 Google Inc. All Rights Reserved.\n//\n//  Licensed under the Apache License, Version 2.0 (the \"License\""
  },
  {
    "path": "internal/graph/testdata/compose1.dot",
    "chars": 560,
    "preview": "digraph \"testtitle\" {\nnode [style=filled fillcolor=\"#f8f8f8\"]\nsubgraph cluster_L { \"label1\" [shape=box fontsize=16 label"
  },
  {
    "path": "internal/graph/testdata/compose2.dot",
    "chars": 620,
    "preview": "digraph \"testtitle\" {\nnode [style=filled fillcolor=\"#f8f8f8\"]\nsubgraph cluster_L { \"label1\" [shape=box fontsize=16 label"
  },
  {
    "path": "internal/graph/testdata/compose3.dot",
    "chars": 859,
    "preview": "digraph \"testtitle\" {\nnode [style=filled fillcolor=\"#f8f8f8\"]\nsubgraph cluster_L { \"label1\" [shape=box fontsize=16 label"
  },
  {
    "path": "internal/graph/testdata/compose4.dot",
    "chars": 182,
    "preview": "digraph \"testtitle\" {\nnode [style=filled fillcolor=\"#f8f8f8\"]\nsubgraph cluster_L { \"label1\" [shape=box fontsize=16 label"
  },
  {
    "path": "internal/graph/testdata/compose5.dot",
    "chars": 850,
    "preview": "digraph \"testtitle\" {\nnode [style=filled fillcolor=\"#f8f8f8\"]\nsubgraph cluster_L { \"label1\" [shape=box fontsize=16 label"
  },
  {
    "path": "internal/graph/testdata/compose6.dot",
    "chars": 601,
    "preview": "digraph \"testtitle\" {\nnode [style=filled fillcolor=\"#f8f8f8\"]\nsubgraph cluster_L { \"label1\" [shape=box fontsize=16 label"
  },
  {
    "path": "internal/graph/testdata/compose7.dot",
    "chars": 624,
    "preview": "digraph \"testtitle\" {\nnode [style=filled fillcolor=\"#f8f8f8\"]\nsubgraph cluster_L { \"label1\" [shape=box fontsize=16 label"
  },
  {
    "path": "internal/graph/testdata/compose9.dot",
    "chars": 672,
    "preview": "digraph \"testtitle\" {\nnode [style=filled fillcolor=\"#f8f8f8\"]\nsubgraph cluster_L { \"comment line 1\\lcomment line 2 \\\"unt"
  },
  {
    "path": "internal/measurement/measurement.go",
    "chars": 9022,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/measurement/measurement_test.go",
    "chars": 2598,
    "preview": "// Copyright 2017 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/plugin/plugin.go",
    "chars": 8118,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/proftest/BUILD",
    "chars": 368,
    "preview": "# Description:\n#   Auto-imported from github.com/google/pprof/internal/proftest\n\nlicenses([\"notice\"])\n\npackage(\n    defa"
  },
  {
    "path": "internal/proftest/proftest.go",
    "chars": 4753,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/report/package.go",
    "chars": 512,
    "preview": "package report\n\nimport \"regexp\"\n\n// pkgRE extracts package name, It looks for the first \".\" or \"::\" that occurs\n// after"
  },
  {
    "path": "internal/report/package_test.go",
    "chars": 1378,
    "preview": "package report\n\nimport (\n\t\"testing\"\n)\n\nfunc TestPackageName(t *testing.T) {\n\ttype testCase struct {\n\t\tname   string\n\t\tex"
  },
  {
    "path": "internal/report/report.go",
    "chars": 39504,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/report/report_test.go",
    "chars": 15471,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/report/shortnames.go",
    "chars": 1832,
    "preview": "// Copyright 2022 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/report/shortnames_test.go",
    "chars": 1489,
    "preview": "package report\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestShortNames(t *testing.T) {\n\ttype testCase struct {\n\t\tname str"
  },
  {
    "path": "internal/report/source.go",
    "chars": 31982,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/report/source_html.go",
    "chars": 1739,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/report/source_test.go",
    "chars": 8059,
    "preview": "//  Copyright 2017 Google Inc. All Rights Reserved.\n//\n//  Licensed under the Apache License, Version 2.0 (the \"License\""
  },
  {
    "path": "internal/report/stacks.go",
    "chars": 6563,
    "preview": "// Copyright 2022 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/report/stacks_test.go",
    "chars": 5653,
    "preview": "package report\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"slices\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/google/pprof/profile\"\n)\n\n// mak"
  },
  {
    "path": "internal/report/synth.go",
    "chars": 883,
    "preview": "package report\n\nimport (\n\t\"github.com/google/pprof/profile\"\n)\n\n// synthCode assigns addresses to locations without an ad"
  },
  {
    "path": "internal/report/synth_test.go",
    "chars": 794,
    "preview": "package report\n\nimport (\n\t\"testing\"\n\n\t\"github.com/google/pprof/profile\"\n)\n\nfunc TestSynthAddresses(t *testing.T) {\n\ts :="
  },
  {
    "path": "internal/report/testdata/README.md",
    "chars": 255,
    "preview": "sample/ contains a sample program that can be profiled.\nsample.bin is its x86-64 binary.\nsample.cpu is a profile generat"
  },
  {
    "path": "internal/report/testdata/sample/sample.go",
    "chars": 1383,
    "preview": "//  Copyright 2017 Google Inc. All Rights Reserved.\n//\n//  Licensed under the Apache License, Version 2.0 (the \"License\""
  },
  {
    "path": "internal/report/testdata/source.dot",
    "chars": 2541,
    "preview": "digraph \"unnamed\" {\nnode [style=filled fillcolor=\"#f8f8f8\"]\nsubgraph cluster_L { \"Duration: 10s, Total samples = 11111 \""
  },
  {
    "path": "internal/report/testdata/source.rpt",
    "chars": 2253,
    "preview": "Total: 11111\nROUTINE ======================== bar in testdata/source1\n        10        110 (flat, cum)  0.99% of Total\n"
  },
  {
    "path": "internal/report/testdata/source1",
    "chars": 298,
    "preview": "source1 line 1;\nsource1 line 2;\nsource1 line 3;\nsource1 line 4;\nsource1 line 5;\nsource1 line 6;\nsource1 line 7;\nsource1 "
  },
  {
    "path": "internal/report/testdata/source2",
    "chars": 298,
    "preview": "source2 line 1;\nsource2 line 2;\nsource2 line 3;\nsource2 line 4;\nsource2 line 5;\nsource2 line 6;\nsource2 line 7;\nsource2 "
  },
  {
    "path": "internal/symbolizer/symbolizer.go",
    "chars": 10549,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/symbolizer/symbolizer_test.go",
    "chars": 12672,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/symbolz/symbolz.go",
    "chars": 5534,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/symbolz/symbolz_test.go",
    "chars": 6449,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "internal/transport/transport.go",
    "chars": 3858,
    "preview": "// Copyright 2018 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "pprof.go",
    "chars": 3185,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "profile/encode.go",
    "chars": 17734,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "profile/filter.go",
    "chars": 7691,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "profile/filter_test.go",
    "chars": 17901,
    "preview": "// Copyright 2018 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "profile/index.go",
    "chars": 1954,
    "preview": "// Copyright 2016 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "profile/index_test.go",
    "chars": 3243,
    "preview": "// Copyright 2016 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "profile/legacy_java_profile.go",
    "chars": 9048,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "profile/legacy_profile.go",
    "chars": 33614,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "profile/legacy_profile_test.go",
    "chars": 9036,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "profile/merge.go",
    "chars": 17486,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "profile/merge_test.go",
    "chars": 11291,
    "preview": "// Copyright 2018 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "profile/profile.go",
    "chars": 23062,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "profile/profile_test.go",
    "chars": 49715,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "profile/proto.go",
    "chars": 7959,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "profile/proto_test.go",
    "chars": 3905,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "profile/prune.go",
    "chars": 5560,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "profile/prune_test.go",
    "chars": 5679,
    "preview": "// Copyright 2014 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");"
  },
  {
    "path": "profile/testdata/cppbench.contention",
    "chars": 1551,
    "preview": "--- contentionz 1 ---\ncycles/second = 3201000000\nsampling period = 100\nms since reset = 16502830\ndiscarded samples = 0\n "
  },
  {
    "path": "profile/testdata/cppbench.contention.string",
    "chars": 2025,
    "preview": "PeriodType: contentions count\nPeriod: 100\nDuration: 4h35\nSamples:\ncontentions/count delay/nanoseconds\n       2700  60888"
  },
  {
    "path": "profile/testdata/cppbench.cpu.string",
    "chars": 6026,
    "preview": "PeriodType: cpu nanoseconds\nPeriod: 10000000\nSamples:\nsamples/count cpu/nanoseconds\n          1   10000000: 1 2 3 4 5 6 "
  },
  {
    "path": "profile/testdata/cppbench.growth",
    "chars": 14357,
    "preview": "heap profile:     85: 178257920 [    85: 178257920] @ growthz\n     1:  2097152 [     1:  2097152] @ 0xb83003 0xb87d50 0x"
  },
  {
    "path": "profile/testdata/cppbench.growth.string",
    "chars": 9523,
    "preview": "PeriodType: space bytes\nPeriod: 1\nSamples:\nobjects/count space/bytes\n          1    2097152: 1 2 3 4 5 6 7 8 9 10 11 12 "
  },
  {
    "path": "profile/testdata/cppbench.heap",
    "chars": 5787,
    "preview": "heap profile:    144:  8498176 [   144:  8498176] @ heapz_v2/524288\n     1:     9216 [     1:     9216] @ 0xc635c8 0x42e"
  },
  {
    "path": "profile/testdata/cppbench.heap.string",
    "chars": 7090,
    "preview": "PeriodType: space bytes\nPeriod: 524288\nSamples:\nobjects/count space/bytes\n         57     528909: 1 2 3 4 5 6 7 8 9 10 1"
  },
  {
    "path": "profile/testdata/cppbench.thread",
    "chars": 1271,
    "preview": "--- threadz 1 ---\n\n--- Thread 7f794ab90940 (name: main/14748) stack: ---\n  PC:  0x00bc8f1c: helper(arg *)\n  0x0040be31: "
  },
  {
    "path": "profile/testdata/cppbench.thread.all",
    "chars": 1242,
    "preview": "--- threadz 1 ---\n\n--- Thread 7eff063d9940 (name: main/25376) stack: ---\n  PC:  0x00bc8f1c: helper(arg*)\n  0x0040be31: m"
  },
  {
    "path": "profile/testdata/cppbench.thread.all.string",
    "chars": 1030,
    "preview": "PeriodType: thread count\nPeriod: 1\nSamples:\nthread/count\n          1: 1 2 3 \n          1: 4 5 6 3 \n          2: 1 5 7 3 "
  }
]

// ... and 39 more files (download for full content)

About this extraction

This page contains the full source code of the google/pprof GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 239 files (1.1 MB), approximately 385.2k tokens, and a symbol index with 1120 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!