Rust · from scratch · a call tree you can read

See where a program
spent its life.

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.

hottest
full run
waits on children (cold) its own work (hot) ring depth = call depth
 

The same profiler, two clocks

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.

1

Enter and exit

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.

2

Self vs total

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.

3

The bloom

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;
}

Read the source, not just the numbers

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