Rust · from scratch · a call tree you can read
Stopwatch is a profiler. It measures the time inside every function, separates a function's own work from time spent in its children, and blooms the whole run into a chronomandala. Pick a workload and watch the time unfold.
Timing is usually the flaky part of a profiler, because wall-clock noise makes small runs unrepeatable. Stopwatch injects the clock. The Rust binary reads the system clock, and the demo above uses a clock that counts units of work, so the bloom is exactly reproducible. Everything else is identical.
Every span records the time on the way in and on the way out. Spans nest, so the profiler always knows which function is running and who called it.
Total time is everything between enter and exit. Self time subtracts the time spent inside children, so a function that only calls others shows almost no self time. That difference is the whole game.
Each function becomes an arc. Its sweep is its share of total time, its ring is its call depth, and its color runs from cold blue (waiting on children) to hot orange (its own work). Hot leaves glow.
// self time is computed per activation, never guessed fn exit(&mut self) { let elapsed = self.clock.now_nanos() - frame.start; node.total_nanos += elapsed; node.self_nanos += elapsed - frame.children_nanos; parent.children_nanos += elapsed; }
The CLI runs real workloads under the system clock and prints an indented call tree with total time, self time, and percent, then names the line where the program spent most of its life.
cargo run -- run --workload sort
cargo run -- run --workload mixed --json # the same tree the bloom reads
$ stopwatch run --workload sort name calls total(ms) self(ms) pct quick_sort 1 0.399 0.173 81.1% partition 2671 0.226 0.226 46.0% bubble_sort 1 0.093 0.093 18.9% Your program spent 46.0% of its life in partition().
perf, flamegraph, and criterion are excellent tools solving adjacent problems. Stopwatch fills the gap between them: an embeddable, deterministic call tree.
An OS-level sampling profiler, Linux only, and not deterministic. Stopwatch is pure Rust, embeds inside your own code, and its clock is injected so a test can assert exact nanosecond totals.
A visualizer for folded stacks. Stopwatch feeds it directly through to_flamegraph(), emitting Brendan Gregg folded-stack format, so it complements that tool rather than replacing it.
Statistical microbenchmarks for timing, not a call tree. Stopwatch gives you a call tree with self time versus total time, and still runs inside a test suite.
An embeddable call-tree profiler with self versus total time, deterministic through an injected clock, one dependency for the CLI and zero for the library.
The same profiler, reachable four ways. Every surface reads the same call tree.
Run a built-in workload with a text report and epitaph, or --json for the raw tree, or --flamegraph for folded stacks.
Wrap work in span for a plain value or try_span for a Result, then read text_report() and epitaph().
Inject MockClock instead of SystemClock and the test suite asserts exact nanosecond totals, no flaky sleeps.
A run_workload tool over the Model Context Protocol returns the report, epitaph, or folded stacks. It never compiles or runs arbitrary code.
cargo build --release stopwatch run --workload mixed stopwatch run --workload sort --flamegraph | flamegraph.pl > sort.svg