Live, low-overhead heap profiler for C/C++.
LD_PRELOAD in → real-time flame graphs, leak detection, and crash backtraces out.
Point sherlock at a C/C++ source file and it compiles it, runs it under an allocator-interposing profiler, and opens a live terminal dashboard showing exactly where your heap memory is going — as it happens.
sherlock <program.c|program.cpp> [extra compiler flags...]One command: compiles the target with the right flags, launches it under the
profiler, and attaches the dashboard. Press q to quit.
- 🔥 Flame graph of live heap by call stack — width and color = share of memory.
- 🩸 Leak detection — probable while running, definite once the target exits.
- 🌡️ Heat map of allocation activity per call site over time.
- 💥 Crash backtraces — catches fatal signals and records a final trace before the core dump.
- 📈 Live stats — peak heap, cumulative bytes, churn, and a heap-over-time sparkline.
- 📤 Export to folded stacks for
flamegraph.pl/ speedscope.
Near-zero target overhead: symbol resolution and rendering happen in a separate process, so the profiled program is never made to wait.
Prerequisites: a Rust toolchain (cargo), a C/C++ compiler (cc / c++),
and Linux (x86_64 for the full feature set; aarch64 supported with caveats).
git clone <repo-url> sherlock && cd sherlock
cargo install --path sherlockThis puts the sherlock command on your PATH (in ~/.cargo/bin).
Keep the clone.
sherlockrebuildslibprofiler.soand the analyzer from source on each run, so the checkout must stay reachable. If you move it, pointSHERLOCK_WORKSPACEat the new location:export SHERLOCK_WORKSPACE=/path/to/sherlock
# Profile a bundled fixture
sherlock testing/leaks.c
# Your own program, passing extra compiler flags through
sherlock myprog.c -O2 -lpthreadPrefer not to install? Run straight from the checkout with
cargo run -p sherlock -- testing/leaks.c.
The dashboard opens attached to the running target. See docs/USAGE.md for every panel, view, and key.
A double-buffered data pipeline across three crates:
| Crate | Output | Role |
|---|---|---|
profiler |
libprofiler.so |
LD_PRELOAD hooks; walks the frame-pointer stack; writes events to shared memory; catches fatal signals |
analyzer |
analyzer (bin) |
drains the ring buffer, resolves addresses to func (file:line), tracks live allocations, draws the TUI |
sherlock |
sherlock (bin) |
orchestrator: builds everything, compiles + launches the target, runs the dashboard |
The producer (libprofiler.so) records each event plus a backtrace into a
lock-free SPSC ring buffer in POSIX shared memory — no symbol resolution on the
hot path. The consumer (analyzer) drains that ring on a dedicated thread
that never blocks on rendering, while the UI thread reads immutable snapshots at
~30 FPS.
Full rationale (dlsym reentrancy, crash guard, memory orderings, ASLR resolution, …) is in DESIGN.md.
| Doc | Contents |
|---|---|
| docs/USAGE.md | Dashboard panels, the four views, keybindings, CLI flags, headless drain, export |
| DESIGN.md | Architecture and internals — the why behind the code |
cargo build # build the workspace
cargo test # unit tests: ring buffer, stack walker, tracker, resolver
./scripts/smoke_test.sh # end-to-end: run a crashing fixture, assert the crash is capturedStack walking is frame-pointer based (sherlock builds targets with
-fno-omit-frame-pointer); stripped or frame-pointer-omitting libraries may
truncate or show raw 0x… addresses. The crash handler and crash-proof walk are
x86_64-only (aarch64 has the walk but not the handler). Under extreme
allocation rates the ring can fill faster than it drains — dropped events are
counted, never silently lost. See docs/USAGE.md for
the full list.
Licensed under either of Apache License, Version 2.0 or MIT license at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.