NAME
Punk::Observe::Flame - aggregated self-time across traces
SYNOPSIS
use Punk::Observe::Flame;
my $f = Punk::Observe::Flame::build(\@spans);
for my $frame (@{ $f->{frames} }) {
printf "%*s%d self %s of %s\n", $frame->{depth} * 2, '',
$frame->{name}, $frame->{self}, $frame->{total};
}
DESCRIPTION
A waterfall shows one trace. A flame graph shows a thousand: the same call paths merged, so what stands out is where the time actually goes rather than where it went once.
Frames are keyed by (service, name, path), so the same operation reached by two different call paths is two frames. Merging them would answer "how slow is this function" and hide "it is only slow when the checkout path calls it", which is the question worth asking.
Self time, not total
self is a frame's duration minus the time its children were running. total is the whole span. Summing total over a tree counts the same nanosecond once per level, so a chart built on it says the root is 100% of everything and tells nobody anything.
total_self across the whole graph is therefore the real denominator.
Traces are folded one at a time, the way the compactor does it, so a partial trace contributes what it has rather than being held back.
FUNCTIONS
build
my $f = Punk::Observe::Flame::build(\@spans);
Builds the aggregated tree from span specs. The span spec is the one in "THE SPAN SPEC" in Punk::Observe::Trace; name and service are symbol numbers.
{
frames => [ { name, service, parent, depth, total, self, count }, ... ],
total_self => '4200000000',
}
parent is an index into frames, or -1 for a root. depth is the nesting level. count is how many spans folded into the frame, which is what distinguishes one very slow call from ten thousand ordinary ones.
Frames come out in tree order, so a parent always precedes its children.