Skip to content

Commit 2c07115

Browse files
authored
Add LLVM/21 support to llvm2lcov (#422)
This LLVM version introduces JSON data format '3.0.1', which adds 'fileId' to MC/DC entries. This enables using branches from expansions for transferring MC/DC coverage data from expansions to their call sites. It also helps to add correct expressions for some MC/DC branches belonging to groups that contain branches from expansions. Signed-off-by: Roman Beliaev <[email protected]>
1 parent 387d45f commit 2c07115

File tree

3 files changed

+213
-67
lines changed

3 files changed

+213
-67
lines changed

bin/llvm2lcov

Lines changed: 111 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,14 @@
3535
# - enable MC/DC instrumentation in your compile/link steps, and
3636
# - pass the '--mcdc-coverage' flag to llvm2lcov
3737
#
38+
# You can also use LLVM/21 or newer to generate MC/DC data more cleanly.
39+
#
3840
# See 'llvm2lcov --help' for more usage information
3941
#
4042
# See the LLVM documentation for more information on flags and compilation options.
4143

4244
use strict;
45+
use version;
4346
use warnings;
4447
require Exporter;
4548

@@ -131,6 +134,7 @@ sub parse
131134
unless (defined($json) &&
132135
exists($json->{data}) &&
133136
'ARRAY' eq ref($json->{data}));
137+
my $json_version = version->parse($json->{version});
134138

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

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

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

162164
my $summary = $f->{summary};
163165
my $branches = $f->{branches};
@@ -235,7 +237,8 @@ sub parse
235237
$currentLine = $isRegionEntry ? $line : $line + 1;
236238
}
237239
}
238-
if ($mcdc) {
240+
if ($mcdc && $json_version < version->parse("3.0.1")) {
241+
my $mcdcData = $fileInfo->testcase_mcdc($testname);
239242
my @mcdcBranches; # array (start line, start column, expression)
240243
foreach my $branch (@$branches) {
241244
die("unexpected branch data")
@@ -316,12 +319,21 @@ sub parse
316319
my $count = $f->{count};
317320
my $regions = $f->{regions}; # startline/col, endline/col/
318321
my $branches = $f->{branches};
322+
# The version "3.0.1" adds fileId to mcdc.
323+
# This allows using MC/DC branches from expansions for placing MC/DC entries defined in expansions to expansions call sites.
324+
my $mcdc = $f->{mcdc_records}
325+
if ($lcovutil::mcdc_coverage &&
326+
$json_version >= version->parse("3.0.1") &&
327+
exists($f->{mcdc_records}));
319328

320329
my $functionMap = $info->testfnc($testname);
321330
# use branch data to derive MC/DC expression - so need
322331
# it, even if user didn't ask
323332
my $branchData = $info->testbr($testname)
324-
if $lcovutil::br_coverage;
333+
if $lcovutil::br_coverage || $mcdc;
334+
my $mcdcData = $info->testcase_mcdc($testname)
335+
if ($json_version >= version->parse("3.0.1") &&
336+
$lcovutil::mcdc_coverage);
325337
my $startLine = $regions->[0]->[0]; # startline of first region
326338
my $endline = $regions->[0]->[2]; # endline of last region
327339
if ($lcovutil::func_coverage) {
@@ -331,6 +343,10 @@ sub parse
331343
unless defined($functionMap->findName($name));
332344
$functionMap->add_count($name, $count);
333345
}
346+
347+
my @mcdcBranches; # array (fileId, start line, start column, expression)
348+
my %expanded_mcdcBranches; # hash of branch's fileId -> branch's start line
349+
334350
if ($branchData) {
335351
my $funcBranchData = BranchData->new();
336352
my $regionIdx = 0;
@@ -379,26 +395,99 @@ sub parse
379395
++$regionIdx;
380396
}
381397
}
382-
# Processed branch on the same line doesn't have to be the previous.
383-
my $brEntry = $funcBranchData->value($line);
384-
my $branchIdx =
385-
!defined($brEntry) ? 0 :
386-
scalar(@{$brEntry->getBlock(0)});
387-
my $br =
388-
BranchBlock->new($branchIdx, $trueCount,
389-
!defined($expr) ? $branchIdx :
390-
"(" . $expr . ") == True");
391-
$funcBranchData->append($line, 0, $br, $filename);
392-
393-
++$branchIdx;
394-
$br =
395-
BranchBlock->new($branchIdx, $falseCount,
396-
!defined($expr) ? $branchIdx :
397-
"(" . $expr . ") == False");
398-
$funcBranchData->append($line, 0, $br, $filename);
398+
$fileId = $b->[6];
399+
# Consider only branches of "MCDCBranchRegion" kind.
400+
if ($mcdc &&
401+
$kind == 6 &&
402+
!defined($expanded_mcdcBranches{$fileId})) {
403+
if ($fileId &&
404+
scalar(@mcdcBranches) &&
405+
$fileId == $mcdcBranches[-1]->[0]) {
406+
pop(@mcdcBranches);
407+
$expanded_mcdcBranches{$fileId} = $line;
408+
} else {
409+
push(@mcdcBranches,
410+
[$fileId, $line, $col, $expr]);
411+
}
412+
}
413+
414+
if ($lcovutil::br_coverage) {
415+
# Processed branch on the same line doesn't have to be the previous.
416+
my $brEntry = $funcBranchData->value($line);
417+
my $branchIdx =
418+
!defined($brEntry) ? 0 :
419+
scalar(@{$brEntry->getBlock(0)});
420+
my $br =
421+
BranchBlock->new($branchIdx, $trueCount,
422+
!defined($expr) ? $branchIdx :
423+
"(" . $expr . ") == True");
424+
$funcBranchData->append($line, 0, $br, $filename);
425+
426+
++$branchIdx;
427+
$br =
428+
BranchBlock->new($branchIdx, $falseCount,
429+
!defined($expr) ? $branchIdx :
430+
"(" . $expr . ") == False");
431+
$funcBranchData->append($line, 0, $br, $filename);
432+
}
433+
}
434+
$branchData->union($funcBranchData)
435+
if $lcovutil::br_coverage;
436+
}
437+
if ($mcdc) {
438+
foreach my $m (@$mcdc) {
439+
die("unexpected MC/DC data") unless scalar(@$m) == 10;
440+
my ($line, $col, $endLine, $endCol,
441+
$trueCount, $falseCount, $fileId, $expandedId,
442+
$kind, $cov) = @$m;
443+
die("unexpected MC/DC cov")
444+
unless 'ARRAY' eq ref($cov);
445+
my $expr;
446+
my @brExprs;
447+
if ($fileId == $expandedId) {
448+
foreach my $branch (@mcdcBranches) {
449+
my ($brFileId, $brLine, $brCol, $brExpr) =
450+
@$branch;
451+
if (($brLine > $line ||
452+
($brLine == $line && $brCol >= $col))
453+
&&
454+
($brLine < $endLine ||
455+
($brLine == $endLine && $brCol <= $endCol))
456+
) {
457+
push(@brExprs, [$brLine, $brCol, $brExpr]);
458+
}
459+
}
460+
@brExprs = sort {$a->[0] <=> $b->[0] ||
461+
$a->[1] <=> $b->[1]
462+
} @brExprs;
463+
$expr =
464+
$srcReader->getExpr($line, $col,
465+
$endLine, $endCol)
466+
if $srcReader->notEmpty();
467+
} else {
468+
$line = $expanded_mcdcBranches{$fileId};
469+
}
470+
my $current_mcdc =
471+
$mcdcData->new_mcdc($mcdcData, $line);
472+
my $groupSize = scalar(@$cov);
473+
my $idx = 0;
474+
foreach my $c (@$cov) {
475+
my $brExpr = $brExprs[$idx]->[2]
476+
if ($fileId == $expandedId &&
477+
$idx < scalar(@brExprs));
478+
my $fullExpr = defined($brExpr) &&
479+
defined($expr) ? "'$brExpr' in '$expr'" : $idx;
480+
$current_mcdc->insertExpr($filename, $groupSize, 0,
481+
$c, $idx, $fullExpr);
482+
$current_mcdc->insertExpr($filename, $groupSize, 1,
483+
$c, $idx, $fullExpr);
484+
++$idx;
485+
}
486+
$mcdcData->close_mcdcBlock($current_mcdc);
399487
}
400-
$branchData->union($funcBranchData);
401488
}
489+
$info->testbr()->remove($testname)
490+
if $mcdc && !$lcovutil::br_coverage;
402491
}
403492
}
404493
lcovutil::info(2, "finished $jsonFile\n");

tests/llvm2lcov/llvm2lcov.sh

Lines changed: 101 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -190,74 +190,131 @@ for line in 33 36 39 44 ; do
190190
done
191191

192192
# check branches total number
193-
grep -E "BRF:54$" test.info
193+
grep -E "BRF:56$" test.info
194194
if [ $? != 0 ] ; then
195195
echo "unexpected total number of branches"
196196
if [ 0 == $KEEP_GOING ] ; then
197197
exit 1
198198
fi
199199
fi
200200
# check branches hit number
201-
grep -E "BRH:34$" test.info
201+
grep -E "BRH:35$" test.info
202202
if [ $? != 0 ] ; then
203203
echo "unexpected hit number of branches"
204204
if [ 0 == $KEEP_GOING ] ; then
205205
exit 1
206206
fi
207207
fi
208208

209-
# line main.cpp:70 should contain 2 groups of MC/DC entries
210-
line=70
211-
MCDC_1=`grep -c "MCDC:$line,2," test.info`
212-
MCDC_2=`grep -c "MCDC:$line,3," test.info`
213-
if [ 4 != "$MCDC_1" ] || [ 6 != "$MCDC_2" ] ; then
214-
echo "did not find expected MC/DC entries on line $line"
215-
if [ 0 == $KEEP_GOING ] ; then
216-
exit 1
209+
# LLVM/21 and later generate JSON data files in the new format.
210+
# So, these files should be processed differently.
211+
IFS='.' read -r -a LLVM_VER <<< `clang -dumpversion`
212+
if [ "${LLVM_VER[0]}" -ge 21 ] ; then
213+
# line main.cpp:70 should contain 2 groups of MC/DC entries
214+
line=70
215+
MCDC_1=`grep -c "MCDC:$line,2," test.info`
216+
MCDC_2=`grep -c "MCDC:$line,3," test.info`
217+
if [ 4 != "$MCDC_1" ] || [ 6 != "$MCDC_2" ] ; then
218+
echo "did not find expected MC/DC entries on line $line"
219+
if [ 0 == $KEEP_GOING ] ; then
220+
exit 1
221+
fi
217222
fi
218-
fi
219-
# check that MC/DC entries have right <expressions>
220-
N=`grep -c "MCDC:63,2,[tf],1,1,'i < 1' in 'a\[i\] && i < 1'" test.info`
221-
if [ 2 != "$N" ] ; then
222-
echo "did not find expected MC/DC entries on line 63"
223-
if [ 0 == $KEEP_GOING ] ; then
224-
exit 1
223+
# check that MC/DC entries have right <expressions>
224+
N=`grep -c "MCDC:40,2,[tf],0,1,'i <= 0' in 'BOOL(i > 0) || i <= 0)'" test.info`
225+
if [ 2 != "$N" ] ; then
226+
echo "did not find expected MC/DC entries on line 40"
227+
if [ 0 == $KEEP_GOING ] ; then
228+
exit 1
229+
fi
225230
fi
226-
fi
227-
# check MC/DC defined in macros
228-
grep -E "MCDC:6,2,[tf]" test.excl.info
229-
if [ 0 != $? ] ; then
230-
echo "did not find expected MC/DC"
231-
if [ 0 == $KEEP_GOING ] ; then
232-
exit 1
231+
# check MC/DC defined in macros
232+
grep -E "MCDC:" test.excl.info
233+
if [ 0 == $? ] ; then
234+
echo "find unexpected MC/DC"
235+
if [ 0 == $KEEP_GOING ] ; then
236+
exit 1
237+
fi
233238
fi
234-
fi
235-
for m in \
236-
"MCDC:6,2,[tf]" \
237-
"MCDC:15,2,[tf]" \
238-
; do
239-
grep -E $m test.info
239+
for line in 33 36 39 ; do
240+
grep -E "MCDC:$line,[23],[tf]" test.info
241+
if [ 0 != $? ] ; then
242+
echo "did not find expected MC/DC on line $line"
243+
if [ 0 == $KEEP_GOING ] ; then
244+
exit 1
245+
fi
246+
fi
247+
done
248+
# check MC/DC total number
249+
grep -E "MCF:40$" test.info
250+
if [ $? != 0 ] ; then
251+
echo "unexpected total number of MC/DC entries"
252+
if [ 0 == $KEEP_GOING ] ; then
253+
exit 1
254+
fi
255+
fi
256+
# check MC/DC hit number
257+
grep -E "MCH:10$" test.info
258+
if [ $? != 0 ] ; then
259+
echo "unexpected hit number of MC/DC entries"
260+
if [ 0 == $KEEP_GOING ] ; then
261+
exit 1
262+
fi
263+
fi
264+
else
265+
# line main.cpp:70 should contain 2 groups of MC/DC entries
266+
line=70
267+
MCDC_1=`grep -c "MCDC:$line,2," test.info`
268+
MCDC_2=`grep -c "MCDC:$line,3," test.info`
269+
if [ 4 != "$MCDC_1" ] || [ 6 != "$MCDC_2" ] ; then
270+
echo "did not find expected MC/DC entries on line $line"
271+
if [ 0 == $KEEP_GOING ] ; then
272+
exit 1
273+
fi
274+
fi
275+
# check that MC/DC entries have right <expressions>
276+
N=`grep -c "MCDC:63,2,[tf],1,1,'i < 1' in 'a\[i\] && i < 1'" test.info`
277+
if [ 2 != "$N" ] ; then
278+
echo "did not find expected MC/DC entries on line 63"
279+
if [ 0 == $KEEP_GOING ] ; then
280+
exit 1
281+
fi
282+
fi
283+
# check MC/DC defined in macros
284+
grep -E "MCDC:6,3,[tf]" test.excl.info
240285
if [ 0 != $? ] ; then
241286
echo "did not find expected MC/DC"
242287
if [ 0 == $KEEP_GOING ] ; then
243288
exit 1
244289
fi
245290
fi
246-
done
247-
# check MC/DC total number
248-
grep -E "MCF:34$" test.info
249-
if [ $? != 0 ] ; then
250-
echo "unexpected total number of MC/DC entries"
251-
if [ 0 == $KEEP_GOING ] ; then
252-
exit 1
291+
for m in \
292+
"MCDC:6,2,[tf]" \
293+
"MCDC:15,2,[tf]" \
294+
; do
295+
grep -E $m test.info
296+
if [ 0 != $? ] ; then
297+
echo "did not find expected MC/DC"
298+
if [ 0 == $KEEP_GOING ] ; then
299+
exit 1
300+
fi
301+
fi
302+
done
303+
# check MC/DC total number
304+
grep -E "MCF:34$" test.info
305+
if [ $? != 0 ] ; then
306+
echo "unexpected total number of MC/DC entries"
307+
if [ 0 == $KEEP_GOING ] ; then
308+
exit 1
309+
fi
253310
fi
254-
fi
255-
# check MC/DC hit number
256-
grep -E "MCH:10$" test.info
257-
if [ $? != 0 ] ; then
258-
echo "unexpected hit number of MC/DC entries"
259-
if [ 0 == $KEEP_GOING ] ; then
260-
exit 1
311+
# check MC/DC hit number
312+
grep -E "MCH:10$" test.info
313+
if [ $? != 0 ] ; then
314+
echo "unexpected hit number of MC/DC entries"
315+
if [ 0 == $KEEP_GOING ] ; then
316+
exit 1
317+
fi
261318
fi
262319
fi
263320

tests/llvm2lcov/main.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ int main() {
3636
macro_2(i, i < 10, i > 0);
3737
i = 0;
3838
macro_3(i < 0);
39-
macro_4(i > 0 && i < 10);
39+
macro_4(i < 0 || i > 0 && i < 10);
4040
if (BOOL(i > 0) ||
4141
i <= 0)
4242
;

0 commit comments

Comments
 (0)