CLI tool
mstange/samply avatar
mstange/samply

samply: a command-line sampling profiler that renders in the Firefox Profiler

Command-line sampling profiler for macOS, Linux, and Windows

4,431 stars104 forksRustApache-2.0

At a glance

What is it?
samply wraps a command, samples its stacks, and opens the result in profiler.firefox.com. It is built for developers profiling their own compiled binaries on macOS, Linux, and Windows, and it has real platform-specific constraints.
Who is it for?
samply fits developers who profile binaries they compiled themselves and want a flame graph without building a UI. It is the wrong tool for profiling macOS system executables, which the README says are blocked by code signing, and for anyone who needs off-CPU samples on Linux, since only on-CPU samples are collected there.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What samply records, and who it is for

samply is a command line CPU profiler. You prepend it to a command, and it records the execution of that command as a subprocess. The README's core example is `samply record ./my-application my-arguments`. When the command finishes, samply opens profiler.firefox.com in your default browser, loads the profile there, and starts a local webserver that serves symbol information and source code to that page.

The audience is narrow and specific: people who can compile the thing they want to profile. The README's guidance for Rust is to profile a binary built in release mode with debug info, and for C++ to include the `-g` flag. That is a profiler aimed at developers working on their own code, not at operators attaching to a production process. The README does mention attaching to running processes on macOS, but only after running `samply setup` once, and again after every samply update, to self-sign the binary.

It is a sampling profiler. It collects stack traces per thread at a sampling interval, with a default of 1000Hz, which is one sample per millisecond. That design answers "where is time going" with statistical evidence rather than exact call counts, which is the standard trade-off for low-overhead profiling.

The record-spawn-serve data flow

The architecture visible in the repository is a Rust workspace with the CLI as the default member and the profiling machinery split into separate crates. `Cargo.toml` lists members including `samply`, `samply-symbols`, `samply-object`, `samply-debugid`, `samply-api`, `fxprof-processed-profile`, `gecko_profile`, `wholesym`, and `wholesym-addr2line`. The `etw-reader` crate is excluded from the workspace with the comment that it should not be compiled on non-Windows, which matches the platform split in the README: Windows uses ETW, and that code is kept out of builds elsewhere.

The data flow is: samply spawns your command, collects samples, and converts them into the Firefox Profiler's processed profile format via the `fxprof-processed-profile` crate. Symbol resolution is handled by `samply-symbols` and `wholesym`. Then a local webserver serves symbol information and source code to the browser page, which is why the profile can show source lines and inline stacks. The README states that all data is kept locally, on disk and in RAM, until you choose to upload your profile. The upload is a deliberate action, not a default.

One platform asymmetry matters. On macOS and Windows, samply collects both on-CPU and off-CPU samples, so you can see which stack was blocking on a lock. On Linux, the README says only on-CPU samples are collected at the moment. If your problem is lock contention or I/O wait on Linux, this tool will not show you the blocking stack.

Installing samply and recording a first profile

There are three installation paths. The prebuilt binary script for macOS and Linux pipes an installer from the v0.13.1 release into a shell:

bash
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/mstange/samply/releases/download/samply-v0.13.1/samply-installer.sh | sh

Windows has a PowerShell equivalent that downloads `samply-installer.ps1` from the same release and executes it. If you prefer to build through the Rust toolchain, `cargo install --locked samply` installs from crates.io, and cloning the repository and running `cargo build --release` produces `./target/release/samply`.

On Linux, the first run will likely fail until you grant access to perf events. The README gives a temporary option that lasts until reboot:

bash
echo '1' | sudo tee /proc/sys/kernel/perf_event_paranoid

A more permanent option is `sudo sysctl kernel.perf_event_paranoid=1`. On Linux 5.8 or later you can try `sudo setcap 'cap_perfmon+ep' \`which samply\``, though the README notes that people have reported mixed results with that approach. If you still see an `mmap failed` error, an `EPERM`, the README suggests raising the mlock limit with `sudo sysctl kernel.perf_event_mlock_kb=2048`.

For a first real profile of Rust code, the README's recommended setup is a cargo profile that inherits release and turns on debug info. Create `~/.cargo/config.toml` with:

toml
[profile.profiling]
inherits = "release"
debug = true

Then build with `cargo build --profile profiling` and record with `samply record ./target/profiling/yourrustprogram`. The browser should open profiler.firefox.com with your profile loaded. You can double-click functions in the call tree to open the source view and see which lines were sampled how many times. Without debug info, the README warns, you lose inline stacks and the working source code view.

Where samply fails: macOS system binaries and Linux gaps

The sharpest limitation is documented plainly. On macOS, samply cannot profile system commands such as `sleep` or system `python`. The reason given is code signing: system executables are signed in a way that blocks the `DYLD_INSERT_LIBRARIES` environment variable, which samply relies on to siphon out the `mach_port` of the process. Binaries you compiled yourself, or unsigned and locally-signed ones such as anything installed by `cargo install` or Homebrew, are fine. So a macOS user who wants to know why the system Python is slow is out of luck with this tool.

On Linux, the constraint is two-part. You need perf events access for unprivileged users, which means either loosening `perf_event_paranoid` or attempting the `CAP_PERFMON` route with its reported mixed results. And even with access granted, you only get on-CPU samples. That is a real functional gap against the macOS and Windows builds, not a configuration issue.

Symbol quality is the other failure mode. samply resolves symbols through the browser page it serves, and on Windows the README recommends pointing at symbol servers, most importantly the Microsoft Symbol Server, with `--windows-symbol-server` and `--breakpad-symbol-server` flags. A profile of a stripped binary with no symbol server configured produces addresses, not function names, and the source view has nothing to show.

samply against perf and Instruments

The closest alternative on Linux is `perf` itself, which samply uses underneath for perf events. The difference in approach is the output and the workflow. `perf record` writes a `perf.data` file that you inspect with `perf report` or convert with `perf script`, and reading a call tree means working in the terminal. samply spawns the process for you, converts the samples into the Firefox Profiler's processed profile format, and hands you a browser UI with flame graphs, timelines, and a source view. If you already have a comfortable `perf` workflow and a script that consumes `perf.data`, samply adds a layer you do not need.

On macOS the alternative is Instruments, which ships with Xcode. Instruments can profile system executables, which samply cannot, and it has its own instrument set beyond CPU sampling. The trade-off runs the other way too: Instruments is a GUI application tied to the Apple toolchain, while samply is a single command you can drop into a build script or a CI job. On Windows, the comparable native option is the Visual Studio profiler or Windows Performance Analyzer, which read ETW data directly. samply's `etw-reader` crate is excluded from non-Windows builds, so the ETW path is Windows-only by construction.

The reason to pick samply over any of these is portability of the workflow. The same `samply record ./binary` invocation works on all three platforms, and the resulting profile opens in the same web UI.

Licence, upgrade cost, and what the repository shows about maintenance

samply is dual licensed under Apache-2.0 and MIT, at your option. The README states that contributions intentionally submitted for inclusion are dual licensed the same way unless you explicitly state otherwise. For most users this is a permissive combination with no copyleft obligation, but if your organisation has a policy about which of the two it accepts, that choice is yours to make. This is not legal advice; read `LICENSE-APACHE` and `LICENSE-MIT` in the repository.

The release cadence visible in the release list is uneven. samply-v0.11.0 is dated 2023-01-06, samply-v0.12.0 is dated 2024-04-16, and samply-v0.13.1 is dated 2025-02-01. The last push to the repository was on 2026-09-22, so work has continued after the 0.13.1 release, but the tagged release is older than the repository activity. If you install through `cargo install --locked samply`, you get the crates.io release, not whatever has landed on `main` since. The prebuilt installer script is versioned to 0.13.1 in its URL, so that path also pins you to the release.

Upgrade cost has one macOS-specific wrinkle that is easy to miss. The README says that to attach to running processes on macOS you run `samply setup` once, and again every time samply is updated, to self-sign the binary. If your team updates tooling through a package manager on a schedule, that step needs to be part of the update procedure or attaching silently stops working. On Linux, raising `perf_event_paranoid` is a system-wide setting, so it is not something samply owns or upgrades; it is a host configuration you carry.

Editorial conclusion

samply fits developers who profile binaries they compiled themselves and want a flame graph without building a UI. It is the wrong tool for profiling macOS system executables, which the README says are blocked by code signing, and for anyone who needs off-CPU samples on Linux, since only on-CPU samples are collected there. Before adopting it, verify perf_event access on your Linux hosts and confirm your release binaries carry debug info, because without it the source view and inline stacks do not work.

Frequently asked questions

What does samply do?

samply is a command line CPU profiler that records a profile of a command you run with `samply record`, then opens the result in the Firefox Profiler at profiler.firefox.com and serves symbols and source code to that page from a local webserver.

How do you use samply?

Prepend `samply record` to the command you want to profile, for example `samply record ./my-application my-arguments`. When the command finishes, samply opens profiler.firefox.com in your default browser with the recorded profile loaded, where you can inspect flame graphs, timelines, and a source view.

Is samply free to use?

The source is dual licensed under Apache-2.0 and MIT, at your option, and it installs from crates.io with cargo or from prebuilt binaries in the project's GitHub releases. There is no paid tier described in the README.

Official sources

  1. Issues
  2. License: Apache-2.0
  3. mstange/samply on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/mstange-samply.svg)](https://hysenlabs.com/projects/mstange-samply)