NAME

Devel::XSCover::Flow - the decisions of the C source, from gcov's flow graph

SYNOPSIS

use Devel::XSCover::Flow;
my $flow      = Devel::XSCover::Flow->new(dir => '.');
my $graph     = $flow->parse_llvm($dump, $files);
my $decisions = $flow->decisions($graph);

DESCRIPTION

gcov's text names the blocks on a line and the count of each arm leaving them, but not where an arm goes, and on a line such as if (!stash || !HvNAME_get(stash)) most of the blocks are the macro's own: its inner tests jump within the line and nothing in the source can take their other arm. The flow graph the compiler wrote beside the counters says where every arc goes. A decision in the source is then read from it the way Devel::Cover reads a Perl one: the arcs that leave the lines of the if are its outcomes, the arcs that stay on them are not.

METHODS

new

my $flow = Devel::XSCover::Flow->new(dir => '.');

dir is where the source files are read from for the decisions.

parse_llvm

my $graph = $flow->parse_llvm($text, $files);

Reads what llvm-cov gcov --dump prints: every function with its blocks, each block's count, its arcs with their counts, and its lines by file. The dump names a file by an index, so a function's own file, given in its header, names the index its first line is under, and an index no function starts in is matched to the file of the gcov text ($files, as Devel::XSCover::Gcov parsed it) whose executable lines it shares most of.

parse_gcc

my $graph = $flow->parse_gcc($json);

Reads what gcov --json-format writes on gcc 9 and later: for every line of every file, the blocks on it and the arcs leaving its conditional blocks, with their destinations and counts. The arcs of a block with one successor are not in it, so the graph is marked incomplete.

Both return the same structure: complete, and under files, for every line of every file, blocks (the blocks on the line), arcs ([from, to, count] for the arcs leaving them) and count (the line's count, from the JSON only).

decisions

my $decisions = $flow->decisions($graph);

For every file in the graph, the decisions of its source: each if, while, for and switch, with the arcs that leave the lines its parentheses span, one arm per destination in the order the compiler laid the destinations out, which puts the arm into the body first. A statement on the same line as the closing parenthesis is a body of its own: with a complete graph its arm is the arcs into it, and with gcc's the line's count less the arms that are seen. A block on the decision's lines with one way out, as clang places the if statement's merge block and a macro's arms, stands for where its chain of such blocks ends: the arcs into it count towards that destination, its own arc not again. Returns a hash of file to a list of { line, arms }, decisions with fewer than two arms left out.

ranges

my @ranges = $flow->ranges(\@source_lines);

The decisions of a source, each with its first line, the line its parentheses end on, and whether a body follows on that line.

clean

my @lines = $flow->clean(\@source_lines);

The source with comments, string and character literals and preprocessor lines blanked, so that an if inside any of them is not a decision.

SEE ALSO

Devel::XSCover::Gcov, Devel::XSCover::DB