Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 111 additions & 22 deletions bin/llvm2lcov
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,14 @@
# - enable MC/DC instrumentation in your compile/link steps, and
# - pass the '--mcdc-coverage' flag to llvm2lcov
#
# You can also use LLVM/21 or newer to generate MC/DC data more cleanly.
#
# See 'llvm2lcov --help' for more usage information
#
# See the LLVM documentation for more information on flags and compilation options.

use strict;
use version;
use warnings;
require Exporter;

Expand Down Expand Up @@ -131,6 +134,7 @@ sub parse
unless (defined($json) &&
exists($json->{data}) &&
'ARRAY' eq ref($json->{data}));
my $json_version = version->parse($json->{version});

lcovutil::info("read $jsonFile\n");

Expand All @@ -156,8 +160,6 @@ sub parse
if (defined($version) && $version ne "");

my $lineData = $fileInfo->test($testname);
my $mcdcData = $fileInfo->testcase_mcdc($testname)
if $lcovutil::mcdc_coverage;

my $summary = $f->{summary};
my $branches = $f->{branches};
Expand Down Expand Up @@ -235,7 +237,8 @@ sub parse
$currentLine = $isRegionEntry ? $line : $line + 1;
}
}
if ($mcdc) {
if ($mcdc && $json_version < version->parse("3.0.1")) {
my $mcdcData = $fileInfo->testcase_mcdc($testname);
my @mcdcBranches; # array (start line, start column, expression)
foreach my $branch (@$branches) {
die("unexpected branch data")
Expand Down Expand Up @@ -316,12 +319,21 @@ sub parse
my $count = $f->{count};
my $regions = $f->{regions}; # startline/col, endline/col/
my $branches = $f->{branches};
# The version "3.0.1" adds fileId to mcdc.
# This allows using MC/DC branches from expansions for placing MC/DC entries defined in expansions to expansions call sites.
my $mcdc = $f->{mcdc_records}
if ($lcovutil::mcdc_coverage &&
$json_version >= version->parse("3.0.1") &&
exists($f->{mcdc_records}));

my $functionMap = $info->testfnc($testname);
# use branch data to derive MC/DC expression - so need
# it, even if user didn't ask
my $branchData = $info->testbr($testname)
if $lcovutil::br_coverage;
if $lcovutil::br_coverage || $mcdc;
my $mcdcData = $info->testcase_mcdc($testname)
if ($json_version >= version->parse("3.0.1") &&
$lcovutil::mcdc_coverage);
my $startLine = $regions->[0]->[0]; # startline of first region
my $endline = $regions->[0]->[2]; # endline of last region
if ($lcovutil::func_coverage) {
Expand All @@ -331,6 +343,10 @@ sub parse
unless defined($functionMap->findName($name));
$functionMap->add_count($name, $count);
}

my @mcdcBranches; # array (fileId, start line, start column, expression)
my %expanded_mcdcBranches; # hash of branch's fileId -> branch's start line

if ($branchData) {
my $funcBranchData = BranchData->new();
my $regionIdx = 0;
Expand Down Expand Up @@ -379,26 +395,99 @@ sub parse
++$regionIdx;
}
}
# Processed branch on the same line doesn't have to be the previous.
my $brEntry = $funcBranchData->value($line);
my $branchIdx =
!defined($brEntry) ? 0 :
scalar(@{$brEntry->getBlock(0)});
my $br =
BranchBlock->new($branchIdx, $trueCount,
!defined($expr) ? $branchIdx :
"(" . $expr . ") == True");
$funcBranchData->append($line, 0, $br, $filename);

++$branchIdx;
$br =
BranchBlock->new($branchIdx, $falseCount,
!defined($expr) ? $branchIdx :
"(" . $expr . ") == False");
$funcBranchData->append($line, 0, $br, $filename);
$fileId = $b->[6];
# Consider only branches of "MCDCBranchRegion" kind.
if ($mcdc &&
$kind == 6 &&
!defined($expanded_mcdcBranches{$fileId})) {
if ($fileId &&
scalar(@mcdcBranches) &&
$fileId == $mcdcBranches[-1]->[0]) {
pop(@mcdcBranches);
$expanded_mcdcBranches{$fileId} = $line;
} else {
push(@mcdcBranches,
[$fileId, $line, $col, $expr]);
}
}

if ($lcovutil::br_coverage) {
# Processed branch on the same line doesn't have to be the previous.
my $brEntry = $funcBranchData->value($line);
my $branchIdx =
!defined($brEntry) ? 0 :
scalar(@{$brEntry->getBlock(0)});
my $br =
BranchBlock->new($branchIdx, $trueCount,
!defined($expr) ? $branchIdx :
"(" . $expr . ") == True");
$funcBranchData->append($line, 0, $br, $filename);

++$branchIdx;
$br =
BranchBlock->new($branchIdx, $falseCount,
!defined($expr) ? $branchIdx :
"(" . $expr . ") == False");
$funcBranchData->append($line, 0, $br, $filename);
}
}
$branchData->union($funcBranchData)
if $lcovutil::br_coverage;
}
if ($mcdc) {
foreach my $m (@$mcdc) {
die("unexpected MC/DC data") unless scalar(@$m) == 10;
my ($line, $col, $endLine, $endCol,
$trueCount, $falseCount, $fileId, $expandedId,
$kind, $cov) = @$m;
die("unexpected MC/DC cov")
unless 'ARRAY' eq ref($cov);
my $expr;
my @brExprs;
if ($fileId == $expandedId) {
foreach my $branch (@mcdcBranches) {
my ($brFileId, $brLine, $brCol, $brExpr) =
@$branch;
if (($brLine > $line ||
($brLine == $line && $brCol >= $col))
&&
($brLine < $endLine ||
($brLine == $endLine && $brCol <= $endCol))
) {
push(@brExprs, [$brLine, $brCol, $brExpr]);
}
}
@brExprs = sort {$a->[0] <=> $b->[0] ||
$a->[1] <=> $b->[1]
} @brExprs;
$expr =
$srcReader->getExpr($line, $col,
$endLine, $endCol)
if $srcReader->notEmpty();
} else {
$line = $expanded_mcdcBranches{$fileId};
}
my $current_mcdc =
$mcdcData->new_mcdc($mcdcData, $line);
my $groupSize = scalar(@$cov);
my $idx = 0;
foreach my $c (@$cov) {
my $brExpr = $brExprs[$idx]->[2]
if ($fileId == $expandedId &&
$idx < scalar(@brExprs));
my $fullExpr = defined($brExpr) &&
defined($expr) ? "'$brExpr' in '$expr'" : $idx;
$current_mcdc->insertExpr($filename, $groupSize, 0,
$c, $idx, $fullExpr);
$current_mcdc->insertExpr($filename, $groupSize, 1,
$c, $idx, $fullExpr);
++$idx;
}
$mcdcData->close_mcdcBlock($current_mcdc);
}
$branchData->union($funcBranchData);
}
$info->testbr()->remove($testname)
if $mcdc && !$lcovutil::br_coverage;
}
}
lcovutil::info(2, "finished $jsonFile\n");
Expand Down
145 changes: 101 additions & 44 deletions tests/llvm2lcov/llvm2lcov.sh
Original file line number Diff line number Diff line change
Expand Up @@ -190,74 +190,131 @@ for line in 33 36 39 44 ; do
done

# check branches total number
grep -E "BRF:54$" test.info
grep -E "BRF:56$" test.info
if [ $? != 0 ] ; then
echo "unexpected total number of branches"
if [ 0 == $KEEP_GOING ] ; then
exit 1
fi
fi
# check branches hit number
grep -E "BRH:34$" test.info
grep -E "BRH:35$" test.info
if [ $? != 0 ] ; then
echo "unexpected hit number of branches"
if [ 0 == $KEEP_GOING ] ; then
exit 1
fi
fi

# line main.cpp:70 should contain 2 groups of MC/DC entries
line=70
MCDC_1=`grep -c "MCDC:$line,2," test.info`
MCDC_2=`grep -c "MCDC:$line,3," test.info`
if [ 4 != "$MCDC_1" ] || [ 6 != "$MCDC_2" ] ; then
echo "did not find expected MC/DC entries on line $line"
if [ 0 == $KEEP_GOING ] ; then
exit 1
# LLVM/21 and later generate JSON data files in the new format.
# So, these files should be processed differently.
IFS='.' read -r -a LLVM_VER <<< `clang -dumpversion`
if [ "${LLVM_VER[0]}" -ge 21 ] ; then
# line main.cpp:70 should contain 2 groups of MC/DC entries
line=70
MCDC_1=`grep -c "MCDC:$line,2," test.info`
MCDC_2=`grep -c "MCDC:$line,3," test.info`
if [ 4 != "$MCDC_1" ] || [ 6 != "$MCDC_2" ] ; then
echo "did not find expected MC/DC entries on line $line"
if [ 0 == $KEEP_GOING ] ; then
exit 1
fi
fi
fi
# check that MC/DC entries have right <expressions>
N=`grep -c "MCDC:63,2,[tf],1,1,'i < 1' in 'a\[i\] && i < 1'" test.info`
if [ 2 != "$N" ] ; then
echo "did not find expected MC/DC entries on line 63"
if [ 0 == $KEEP_GOING ] ; then
exit 1
# check that MC/DC entries have right <expressions>
N=`grep -c "MCDC:40,2,[tf],0,1,'i <= 0' in 'BOOL(i > 0) || i <= 0)'" test.info`
if [ 2 != "$N" ] ; then
echo "did not find expected MC/DC entries on line 40"
if [ 0 == $KEEP_GOING ] ; then
exit 1
fi
fi
fi
# check MC/DC defined in macros
grep -E "MCDC:6,2,[tf]" test.excl.info
if [ 0 != $? ] ; then
echo "did not find expected MC/DC"
if [ 0 == $KEEP_GOING ] ; then
exit 1
# check MC/DC defined in macros
grep -E "MCDC:" test.excl.info
if [ 0 == $? ] ; then
echo "find unexpected MC/DC"
if [ 0 == $KEEP_GOING ] ; then
exit 1
fi
fi
fi
for m in \
"MCDC:6,2,[tf]" \
"MCDC:15,2,[tf]" \
; do
grep -E $m test.info
for line in 33 36 39 ; do
grep -E "MCDC:$line,[23],[tf]" test.info
if [ 0 != $? ] ; then
echo "did not find expected MC/DC on line $line"
if [ 0 == $KEEP_GOING ] ; then
exit 1
fi
fi
done
# check MC/DC total number
grep -E "MCF:40$" test.info
if [ $? != 0 ] ; then
echo "unexpected total number of MC/DC entries"
if [ 0 == $KEEP_GOING ] ; then
exit 1
fi
fi
# check MC/DC hit number
grep -E "MCH:10$" test.info
if [ $? != 0 ] ; then
echo "unexpected hit number of MC/DC entries"
if [ 0 == $KEEP_GOING ] ; then
exit 1
fi
fi
else
# line main.cpp:70 should contain 2 groups of MC/DC entries
line=70
MCDC_1=`grep -c "MCDC:$line,2," test.info`
MCDC_2=`grep -c "MCDC:$line,3," test.info`
if [ 4 != "$MCDC_1" ] || [ 6 != "$MCDC_2" ] ; then
echo "did not find expected MC/DC entries on line $line"
if [ 0 == $KEEP_GOING ] ; then
exit 1
fi
fi
# check that MC/DC entries have right <expressions>
N=`grep -c "MCDC:63,2,[tf],1,1,'i < 1' in 'a\[i\] && i < 1'" test.info`
if [ 2 != "$N" ] ; then
echo "did not find expected MC/DC entries on line 63"
if [ 0 == $KEEP_GOING ] ; then
exit 1
fi
fi
# check MC/DC defined in macros
grep -E "MCDC:6,3,[tf]" test.excl.info
if [ 0 != $? ] ; then
echo "did not find expected MC/DC"
if [ 0 == $KEEP_GOING ] ; then
exit 1
fi
fi
done
# check MC/DC total number
grep -E "MCF:34$" test.info
if [ $? != 0 ] ; then
echo "unexpected total number of MC/DC entries"
if [ 0 == $KEEP_GOING ] ; then
exit 1
for m in \
"MCDC:6,2,[tf]" \
"MCDC:15,2,[tf]" \
; do
grep -E $m test.info
if [ 0 != $? ] ; then
echo "did not find expected MC/DC"
if [ 0 == $KEEP_GOING ] ; then
exit 1
fi
fi
done
# check MC/DC total number
grep -E "MCF:34$" test.info
if [ $? != 0 ] ; then
echo "unexpected total number of MC/DC entries"
if [ 0 == $KEEP_GOING ] ; then
exit 1
fi
fi
fi
# check MC/DC hit number
grep -E "MCH:10$" test.info
if [ $? != 0 ] ; then
echo "unexpected hit number of MC/DC entries"
if [ 0 == $KEEP_GOING ] ; then
exit 1
# check MC/DC hit number
grep -E "MCH:10$" test.info
if [ $? != 0 ] ; then
echo "unexpected hit number of MC/DC entries"
if [ 0 == $KEEP_GOING ] ; then
exit 1
fi
fi
fi

Expand Down
2 changes: 1 addition & 1 deletion tests/llvm2lcov/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ int main() {
macro_2(i, i < 10, i > 0);
i = 0;
macro_3(i < 0);
macro_4(i > 0 && i < 10);
macro_4(i < 0 || i > 0 && i < 10);
if (BOOL(i > 0) ||
i <= 0)
;
Expand Down