cargo-treemap
An interactive treemap of what's in your Rust binary, and how much of each dependency you use. It's webpack-bundle-analyzer for cargo.

Every rectangle is a symbol, nested under its module and crate and sized by its bytes in the
compiled artifact. Click a cell to zoom in, switch the weighting between .text, .data,
and gzip, search, and toggle crates in the sidebar. The report is a single self-contained
HTML file; it needs no server or network.
Why
cargo-bloat tells you which crates are big. cargo-treemap adds two things. The first is a
zoomable treemap of the binary down to the symbol. The second is a measure of how much of
each dependency's public API you call: a 400 KB dependency you use three functions of is a
different decision from a 400 KB dependency you lean on heavily, and cargo-treemap tells
them apart and flags the ones that are large but barely used.
Usage
# workspaces & selection
# other targets (see "Targets" below)
# attribute code by its source (definition site) instead of by symbol name
# add per-crate compile time (does a full from-scratch timed build)
Source-based attribution (--dwarf)
By default a symbol is credited to the crate whose name heads it, which for a generic
function is the crate that instantiated it. --dwarf reads debug info and credits code to
the crate whose source defines it. That is usually what you want for "which dependency is
bloating me": a heavily-generic dependency like serde_json, whose code gets monomorphized
all over your own crates, shows up under serde_json instead of scattered across its
callers. It forces a debug build and can move hundreds of KB between crates. It only refines
code; anonymous rodata has no line info and stays in [runtime].
Dependency API usage
--api-usage adds a dependencies panel (button in the top bar, or open the report at
#deps) and a section to the terminal and JSON output:

For each direct dependency it shows the size of its public API (surface), how many of
those functions you use (used), the ratio, its size in your binary, and a verdict that
flags crates that are large but barely used.
Read --accurate before trusting the numbers. Without it, the "used" count comes from the
optimized binary, where the compiler has inlined most cross-crate calls out of existence, so
it is a lower bound and can wrongly flag a crate as underused. --accurate does a second
opt-level-0 build (-C symbol-mangling-version=v0) where those calls survive as symbols,
giving true ratios. The report labels which mode produced it.
Metrics
The treemap can be weighted by any of these. Every node carries all of them, so switching is instant.
| Metric | What it is |
|---|---|
text |
Bytes in executable-code sections (.text). The default, and the closest analog to webpack's parsed size. |
data |
Bytes in data and read-only-data sections. |
gzip |
Gzipped size of each symbol's bytes. |
mono |
Number of symbols and monomorphizations, showing where generic instantiation piles up. |
stat |
Source size per crate (the webpack "stat" analog). |
A crates panel (button in the top bar, or open the report at #crates) holds a per-crate
table: binary size, source LOC and bytes, unsafe count, monomorphizations, dependency kind,
and, with --compile-time, how long each crate took to build. Crates whose code runs only at
compile time (proc-macros and their closure) are labeled compile-only: they cost build time
but ship zero bytes. syn is often your largest crate by source, takes over a second to
compile, and contributes nothing to the binary. A color-by-dep-kind toggle recolors the
treemap to match.
The dependency panel adds the API-usage ratio and value-to-size verdict per dependency.
--compile-time reads per-crate build time from cargo build --timings. It does a full
from-scratch build in a separate target dir, so it's slow, but it shows which crates (often
proc-macros with no runtime footprint) dominate your build.
Comparing builds
Save a baseline and diff against it later:
# on the baseline commit
# after your change
Or compare straight against a git ref, which builds it in a throwaway worktree so your working tree is untouched:
The diff aligns the two trees by crate, module, and symbol and reports the signed size change. The HTML view shows only what changed, sized by the magnitude of the change and colored red where it grew and green where it shrank. The JSON delta is easy to gate on in CI.
Feature size attribution
This builds the package once with --no-default-features, then once per feature with that
feature added, and reports the marginal binary size each feature costs. Features can enable
one another, so the numbers are relative to the no-default baseline and do not sum.
Targets
The size layer reads the compiled artifact with the object crate, so it is
format-agnostic and analyzes a foreign binary regardless of your host:
| Target format | Status |
|---|---|
| Mach-O (macOS) | full |
| ELF (Linux) | full |
WASM (wasm32-*) |
size is honest; per-symbol coverage is partial (a dedicated extractor is planned) |
Building a foreign target still needs a working cross-linker such as cargo-zigbuild or
cross. If you already have the binary, --artifact <path> skips the build and analyzes
it directly, which is handy for CI artifacts and cross-compiled outputs.
How attribution works, and where it's approximate
By default each symbol is attributed to a crate in this order: an exact match against the
dependency and std rlib symbol tables (which blames a monomorphized generic on the crate
that instantiated it), then the first crate name in its demangled path. Symbols that name
no crate (compiler-outlined functions, _main, anonymous constants) go to [runtime];
per-section slack that belongs to no symbol goes to [unattributed]. --dwarf switches code
attribution to the source-definition site instead.
Known approximations, all shown in the output:
- Anonymous read-only data (Unicode tables, regex DFAs, string literals) often has no
crate-attributable symbol name and pools in
[runtime].--dwarfdoes not fix this, because.debug_lineonly covers code addresses. - Inlining and LTO erase symbols, so
.textunder-counts by whatever got inlined. - API usage is measured by function, so trait and derive crates like
serde, and re-export facades likeclap(which re-exportsclap_builder), read as "no measurable fns". Matching is by function name, which can over-count slightly on collisions.
Output formats
--format html(default): the interactive self-contained report.--format table: a ranked terminal breakdown, likecargo-bloat.--format json: the full node tree plus the dependency and per-crate analysis, with a stable schema you can diff between runs to catch size regressions.
License
Dual-licensed under MIT or Apache-2.0, at your option.