Skip to main content
skill CX Codex

rust-profiling — Claude Code skill for Rust performance work

A skill that walks an agent through Rust profiling: flamegraphs, binary size, monomorphization bloat and Criterion microbenchmarks.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add mohitmishra786/low-level-dev-skills --skill rust-profiling -a claude-code
Open on GitHub

What rust-profiling does

The skill turns profiling questions into a fixed sequence. First you build with debug symbols, because a release build without them gives you an unreadable profile — either a release-with-debug profile in Cargo.toml or CARGO_PROFILE_RELEASE_DEBUG=true on the command line. Then flamegraphs through cargo flamegraph, which uses perf on Linux and DTrace on macOS; both want elevated permissions.

Binary size and monomorphization get their own sections. cargo bloat ranks functions and crates by how much space they take, and cargo llvm-lines counts LLVM IR lines and copies per function. A high Copies number means a generic got expanded for every type it was called with. The skill shows the usual fix: keep a thin generic wrapper and move the real work into a concrete inner function.

For timing there is Criterion, configured with harness = false and black_box so the optimizer does not delete the code under test. The rest covers perf record and perf report, plus heaptrack and DHAT for allocation tracking. One thing to plan for: cargo flamegraph is not bundled with the toolchain, you install it with cargo install flamegraph, and on Linux perf needs perf_event_paranoid set to 1 before it will collect samples.

What it helps with

  • generate a flamegraph for a Rust binary
  • track down what made a binary grow
  • count monomorphization copies per function
  • write a Criterion benchmark with black_box
  • inspect allocations with heaptrack or DHAT

Example prompts for the agent

  • Generate a flamegraph for myapp and show me the hottest functions
  • Run cargo bloat --release --crates and tell me which crate is the biggest
  • Write a Criterion benchmark for process across several input sizes

How to install rust-profiling

1Skills CLI (Vercel)

npx skills add mohitmishra786/low-level-dev-skills --skill rust-profiling -a claude-code

2Manually: put the files in place

~/.claude/skills/rust-profiling/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/rust-profiling/

Step by step, with commands for each platform: How to install skills in Claude Code: five ways

Questions about rust-profiling

How do I install rust-profiling in Claude?

Run npx skills add mohitmishra786/low-level-dev-skills --skill rust-profiling -a claude-code. The skill lands at ~/.claude/skills/rust-profiling/SKILL.md.

What does rust-profiling do?

It guides an agent through Rust profiling: debug-symbol builds, flamegraphs, binary size analysis, monomorphization measurement and Criterion microbenchmarks.

What do I need before using it?

The tools are separate installs: cargo install flamegraph, cargo install cargo-bloat, cargo install cargo-llvm-lines. Flamegraphs also need perf with perf_event_paranoid at 1 on Linux, or sudo for DTrace on macOS.

Source text

SKILL.md · MIT
---
name: rust-profiling
description: Rust profiling skill for performance analysis. Use when generating flamegraphs from Rust binaries, measuring monomorphization bloat with cargo-llvm-lines, analysing binary size with cargo-bloat, microbenchmarking with Criterion, or interpreting inlined frames in profiles. Activates on queries about cargo flamegraph, cargo-bloat, cargo-llvm-lines, Criterion benchmarks, Rust performance profiling, or binary size analysis.
---

# Rust Profiling

## Purpose

Guide agents through Rust performance profiling: flamegraphs via cargo-flamegraph, binary size analysis, monomorphization bloat measurement, Criterion microbenchmarks, and interpreting profiling results with inlined Rust frames.

## Triggers

- "How do I generate a flamegraph for a Rust program?"
- "My Rust binary is huge — how do I find what's causing it?"
- "How do I write Criterion benchmarks?"
- "How do I measure monomorphization bloat?"
- "Rust performance is worse than expected — how do I profile it?"
- "How do I use perf with Rust?"

## Workflow

### 1. Build for profiling

```bash
# Release with debug symbols (needed for readable profiles)
# Cargo.toml:
[profile.release-with-debug]
inherits = "release"
debug = true

cargo build --profile release-with-debug

# Or quick: release + debug info inline
CARGO_PROFILE_RELEASE_DEBUG=true cargo build --release
```

### 2. Flamegraphs with cargo-flamegraph

```bash
# Install
cargo install flamegraph

# Linux: uses perf (requires perf_event_paranoid ≤ 1)
sudo sh -c 'echo 1 > /proc/sys/kernel/perf_event_paranoid'
cargo flamegraph --bin myapp -- arg1 arg2

# macOS: uses DTrace (requires sudo)
sudo cargo flamegraph --bin myapp -- arg1 arg2

# Profile tests
cargo flamegraph --test mytest -- test_filter

# Profile benchmarks
cargo flamegraph --bench mybench -- --bench

# Output
# Generates flamegraph.svg in current directory
# Open in browser: firefox flamegraph.svg
```

Custom flamegraph options:
```bash
# More samples
cargo flamegraph --freq 1000 --bin myapp

# Filter to specific threads
cargo flamegraph --bin myapp -- args 2>/dev/null

# Using perf directly for more control
perf record -g -F 999 ./target/release-with-debug/myapp args
perf script | stackcollapse-perf.pl | flamegraph.pl > out.svg
```

### 3. Binary size analysis with cargo-bloat

```bash
# Install
cargo install cargo-bloat

# Show top functions by size
cargo bloat --release -n 20

# Show per-crate size breakdown
cargo bloat --release --crates

# Include only specific crate
cargo bloat --release --filter myapp

# Compare before/after a change
cargo bloat --release --crates > before.txt
# make changes
cargo bloat --release --crates > after.txt
diff before.txt after.txt
```

Typical output:
```
 File  .text    Size    Crate Name
 2.4%   3.0% 47.0KiB      std <std macros>
 1.8%   2.3% 35.5KiB   myapp myapp::heavy_module::process
 1.2%   1.5% 23.1KiB    serde serde::de::...
```

### 4. Monomorphization bloat with cargo-llvm-lines

```bash
# Install
cargo install cargo-llvm-lines

# Show LLVM IR line counts (proxy for monomorphization)
cargo llvm-lines --release | head -40

# Filter to your crate only
cargo llvm-lines --release | grep '^myapp'
```

Typical output:
```
   Lines      Copies  Function name
   85330           1  [LLVM passes]
    7761          92  core::fmt::write
    4672          11  myapp::process::<impl MyTrait for T>
    3201          47  <alloc::vec::Vec<T> as core::ops::Drop>::drop
```

High `Copies` count = monomorphization expansion. Fix:
```rust
// Before: generic, gets monomorphized for every T
fn process<T: AsRef<[u8]>>(data: T) -> usize {
    do_work(data.as_ref())
}

// After: thin generic wrapper + concrete inner
fn process<T: AsRef<[u8]>>(data: T) -> usize {
    fn inner(data: &[u8]) -> usize { do_work(data) }
    inner(data.as_ref())
}
```

### 5. Criterion microbenchmarks

```toml
# Cargo.toml
[dev-dependencies]
criterion = { version = "0.5", features = ["html_reports"] }

[[bench]]
name = "my_bench"
harness = false
```

```rust
// benches/my_bench.rs
use criterion::{black_box, criterion_group, criterion_main, Criterion, BenchmarkId};

fn bench_process(c: &mut Criterion) {
    // Simple benchmark
    c.bench_function("process 1000 items", |b| {
        let data: Vec<i32> = (0..1000).collect();
        b.iter(|| process(black_box(&data)))  // black_box prevents optimization
    });
}

fn bench_sizes(c: &mut Criterion) {
    let mut group = c.benchmark_group("process_sizes");

    for size in [100, 1000, 10000].iter() {
        let data: Vec<i32> = (0..*size).collect();
        group.bench_with_input(
            BenchmarkId::from_parameter(size),
            &data,
            |b, data| b.iter(|| process(black_box(data))),
        );
    }
    group.finish();
}

criterion_group!(benches, bench_process, bench_sizes);
criterion_main!(benches);
```

```bash
# Run all benchmarks
cargo bench

# Run specific benchmark
cargo bench --bench my_bench

# Run with filter
cargo bench -- process_sizes

# Compare with baseline (save/load)
cargo bench -- --save-baseline before
# make changes
cargo bench -- --baseline before

# View HTML report
open target/criterion/report/index.html
```

### 6. perf with Rust (Linux)

```bash
# Record
perf record -g ./target/release-with-debug/myapp args
perf record -g -F 999 ./target/release-with-debug/myapp args  # higher freq

# Report
perf report                     # interactive TUI
perf report --stdio --no-call-graph | head -40   # text

# Annotate specific function
perf annotate myapp::hot_function

# stat (quick counters)
perf stat ./target/release/myapp args
```

Rust-specific perf tips:
- Build with `debug = 1` (line tables only) for faster builds with line-level attribution
- Use `RUSTFLAGS="-C force-frame-pointers=yes"` for better call graphs without DWARF unwinding
- Disable ASLR for reproducible addresses: `setarch $(uname -m) -R ./myapp`

### 7. heaptrack / DHAT for allocations

```bash
# heaptrack (Linux)
heaptrack ./target/release/myapp args
heaptrack_print heaptrack.myapp.*.zst | head -50

# DHAT via Valgrind
valgrind --tool=dhat ./target/debug/myapp args
# Open dhat-out.* with dh_view.html
```

For flamegraph setup and Criterion configuration, see [references/cargo-flamegraph-setup.md](references/cargo-flamegraph-setup.md).

## Related skills

- Use `skills/rust/rustc-basics` for build configuration (debug symbols, profiles)
- Use `skills/profilers/linux-perf` for perf fundamentals
- Use `skills/profilers/flamegraphs` for reading and interpreting flamegraph SVGs
- Use `skills/profilers/valgrind` for allocation profiling with massif/DHAT

© mohitmishra786 · MIT · mohitmishra786/low-level-dev-skills

Similar skills

All skills for Programming languages →

From the same set79

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor