Skip to content

Commit 6414c2b

Browse files
committed
Combine function end line derivation with filtering.
Signed-off-by: Henry Cox <[email protected]>
1 parent f18d34d commit 6414c2b

File tree

2 files changed

+183
-148
lines changed

2 files changed

+183
-148
lines changed

lib/lcovutil.pm

Lines changed: 182 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -746,8 +746,9 @@ sub merge_child_profile($)
746746
if (exists($lcovutil::profileData{$key}{$f})
747747
&&
748748
grep(/^$key$/,
749-
( 'version', 'parse', 'append', 'total',
750-
'resolve'))
749+
( 'version', 'parse',
750+
'append', 'total',
751+
'resolve', 'derive_end'))
751752
) {
752753
$lcovutil::profileData{$key}{$f} += $t;
753754
} else {
@@ -1325,7 +1326,7 @@ sub parseOptions
13251326
[\@opt_ignore_errors, \@rc_ignore],
13261327
[\@lcovutil::exclude_file_patterns, \@rc_exclude_patterns],
13271328
[\@lcovutil::include_file_patterns, \@rc_include_patterns],
1328-
[\@lcovutil::subst_file_patterns, \@rc_subst_patterns],
1329+
[\@lcovutil::file_subst_patterns, \@rc_subst_patterns],
13291330
[\@lcovutil::omit_line_patterns, \@rc_omit_patterns],
13301331
[\@lcovutil::exclude_function_patterns, \@rc_erase_patterns
13311332
],
@@ -4961,8 +4962,7 @@ sub parseLines
49614962
++$lcovutil::cov_filter[$lcovutil::FILTER_DIRECTIVE]->[0];
49624963
++$lcovutil::cov_filter[$lcovutil::FILTER_DIRECTIVE]->[1];
49634964
push(@excluded, 3); #everything excluded
4964-
lcovutil::info(2,
4965-
"exclude '#$1' directive on $filename:$line\n");
4965+
lcovutil::info(2, "exclude '#$1' directive on $filename:$line\n");
49664966
next;
49674967
}
49684968

@@ -5660,9 +5660,152 @@ sub _eraseFunctions
56605660
return $modified;
56615661
}
56625662

5663+
sub _deriveFunctionEndLines
5664+
{
5665+
my $traceInfo = shift;
5666+
my $modified = 0;
5667+
5668+
my $start = Time::HiRes::gettimeofday();
5669+
my @lines = sort { $a <=> $b } $traceInfo->sum()->keylist();
5670+
# sort functions by start line number
5671+
# ignore lambdas - which we don't process correctly at the moment
5672+
# (would need to do syntactic search for the end line)
5673+
my @functions = sort { $a->line() <=> $b->line() }
5674+
grep({ !$_->isLambda() } $traceInfo->func()->valuelist());
5675+
5676+
my $currentLine = @lines ? shift(@lines) : 0;
5677+
my $funcData = $traceInfo->testfnc();
5678+
FUNC: while (@functions) {
5679+
my $func = shift(@functions);
5680+
my $first = $func->line();
5681+
my $end = $func->end_line();
5682+
while ($first < $currentLine) {
5683+
if (@lines) {
5684+
$currentLine = shift @lines;
5685+
} else {
5686+
if (!defined($end)) {
5687+
my $suffix =
5688+
lcovutil::explain_once('derive_end_line',
5689+
" See lcovrc man entry for 'derive_function_end_line'."
5690+
);
5691+
lcovutil::ignorable_error(
5692+
$lcovutil::ERROR_INCONSISTENT_DATA,
5693+
'"' . $traceInfo->filename() .
5694+
"\":$first: function " . $func->name() .
5695+
" found on line but no corresponding 'line' coverage data point. Cannot derive function end line."
5696+
. $suffix);
5697+
}
5698+
next FUNC;
5699+
}
5700+
}
5701+
if (!defined($end)) {
5702+
# where is the next function? Find the last 'line' coverpoint
5703+
# less than the start line of that function..
5704+
if (@lines) {
5705+
# if there are no more lines in this file - then everything
5706+
# must be ending on the last line we saw
5707+
if (@functions) {
5708+
my $next_func = $functions[0];
5709+
my $start = $next_func->line();
5710+
while (@lines &&
5711+
$lines[0] < $start) {
5712+
$currentLine = shift @lines;
5713+
}
5714+
} else {
5715+
# last line in the file must be the last line
5716+
# of this function
5717+
if (@lines) {
5718+
$currentLine = $lines[-1];
5719+
} else {
5720+
my $suffix = lcovutil::explain_once('derive_end_line',
5721+
" See lcovrc man entry for 'derive_function_end_line'."
5722+
);
5723+
lcovutil::ignorable_error(
5724+
$lcovutil::ERROR_INCONSISTENT_DATA,
5725+
'"' . $traceInfo->filenname() .
5726+
"\":$first: function " . $func->name() .
5727+
": last line in file is not last line of function.$suffix"
5728+
);
5729+
next FUNC;
5730+
}
5731+
}
5732+
} elsif ($currentLine < $first) {
5733+
# we ran out of lines in the data...check for inconsistency
5734+
my $suffix =
5735+
lcovutil::explain_once('derive_end_line',
5736+
" See lcovrc man entry for 'derive_function_end_line'.");
5737+
lcovutil::ignorable_error($lcovutil::ERROR_INCONSISTENT_DATA,
5738+
'"' . $traceInfo->filename() .
5739+
"\":$first: function " . $func->name() .
5740+
" found on line but no corresponding 'line' coverage data point. Cannot derive function end line."
5741+
. $suffix);
5742+
5743+
# last FUNC; # quit looking here - all the other functions after this one will have same issue
5744+
next FUNC; # warn about them all
5745+
}
5746+
lcovutil::info(1,
5747+
'"' . $traceInfo->filename() .
5748+
"\":$currentLine: assign end_line " .
5749+
$func->name() . "\n");
5750+
$func->set_end_line($currentLine);
5751+
$modified = 1;
5752+
}
5753+
# we may not have set the end line above due to inconsistency
5754+
# but we also might not have line data
5755+
# - see .../tests/lcov/extract with gcc/4.8
5756+
if (!defined($func->end_line())) {
5757+
my $suffix =
5758+
lcovutil::explain_once('derive_end_line',
5759+
" See lcovrc man entry for 'derive_function_end_line'.");
5760+
lcovutil::ignorable_error($lcovutil::ERROR_INCONSISTENT_DATA,
5761+
'"' . $func->filename() . '":' . $func->line() .
5762+
': failed to set end line for function ' .
5763+
$func->name() . '.' . $suffix);
5764+
next FUNC;
5765+
}
5766+
5767+
# now look for this function in each testcase -
5768+
# set the same endline (if not already set)
5769+
my $key = $func->file() . ':' . $first;
5770+
foreach my $tn ($funcData->keylist()) {
5771+
my $d = $funcData->value($tn);
5772+
my $f = $d->findKey($key);
5773+
if (defined($f)) {
5774+
if (!defined($f->end_line())) {
5775+
$f->set_end_line($func->end_line());
5776+
$modified = 1;
5777+
} else {
5778+
if ($f->end_line() != $func->end_line()) {
5779+
lcovutil::ignorable_error(
5780+
$lcovutil::ERROR_INCONSISTENT_DATA,
5781+
'"' . $func->file() .
5782+
'":' . $first . ': function \'' .
5783+
$func->name() . ' last line is ' .
5784+
$func->end_line() . ' but is ' .
5785+
$f->end_line() . " in testcase '$tn'"
5786+
);
5787+
}
5788+
}
5789+
}
5790+
} #foreach testcase
5791+
} # for each function
5792+
my $end = Time::HiRes::gettimeofday();
5793+
$lcovutil::profileData{derive_end}{$traceInfo->filename()} = $end - $start;
5794+
return $modified;
5795+
}
5796+
56635797
sub _filterFile
56645798
{
5665-
my ($traceInfo, $source_file, $srcReader, $state) = @_;
5799+
my ($traceInfo, $source_file, $actions, $srcReader, $state) = @_;
5800+
5801+
my $modified = 0;
5802+
5803+
if (0 != ($actions & DID_DERIVE)) {
5804+
$modified = _deriveFunctionEndLines($traceInfo);
5805+
if (0 == ($actions & DID_FILTER)) {
5806+
return [$traceInfo, $modified];
5807+
}
5808+
}
56665809
my $region = $cov_filter[$FILTER_EXCLUDE_REGION];
56675810
my $branch_region = $cov_filter[$FILTER_EXCLUDE_BRANCH];
56685811
my $range = $cov_filter[$lcovutil::FILTER_LINE_RANGE];
@@ -5696,7 +5839,6 @@ sub _filterFile
56965839
return ($traceInfo, 0);
56975840
}
56985841

5699-
my $modified = 0;
57005842
if (defined($lcovutil::func_coverage) &&
57015843
!$state->[0]->[1] &&
57025844
(0 != scalar(@lcovutil::exclude_function_patterns) ||
@@ -6306,6 +6448,12 @@ sub applyFilters
63066448
# due to differences in #ifdefs when the corresponding tests were compiled.
63076449
my @filter_workList;
63086450

6451+
my $computeEndLine =
6452+
(0 == ($self->[STATE] & DID_DERIVE) &&
6453+
defined($lcovutil::derive_function_end_line) &&
6454+
$lcovutil::derive_function_end_line != 0 &&
6455+
defined($lcovutil::func_coverage));
6456+
63096457
foreach my $name ($self->files()) {
63106458
my $traceInfo = $self->data($name);
63116459
die("expected TraceInfo, got '" . ref($traceInfo) . "'")
@@ -6323,152 +6471,38 @@ sub applyFilters
63236471
# Jacoco pretends to report function end line - but it appears
63246472
# to be the last line executed - not the actual last line of
63256473
# the function - so broken/completely useless.
6326-
DERIVE:
6327-
if (0 == ($self->[STATE] & DID_DERIVE) &&
6328-
defined($lcovutil::derive_function_end_line) &&
6329-
$lcovutil::derive_function_end_line != 0 &&
6330-
defined($lcovutil::func_coverage) &&
6331-
($lcovutil::derive_end_line_all_files ||
6474+
my $actions = 0;
6475+
if ($computeEndLine &&
6476+
($lcovutil::derive_function_end_line_all_files ||
63326477
is_language('c|java|perl', $source_file))
63336478
) {
6334-
my @lines = sort { $a <=> $b } $traceInfo->sum()->keylist();
6335-
# sort functions by start line number
6336-
# ignore lambdas - which we don't process correctly at the moment
6337-
# (would need to do syntactic search for the end line)
6338-
my @functions = sort { $a->line() <=> $b->line() }
6339-
grep({ !$_->isLambda() } $traceInfo->func()->valuelist());
6340-
6341-
my $currentLine = @lines ? shift(@lines) : 0;
6342-
my $funcData = $traceInfo->testfnc();
6343-
FUNC: while (@functions) {
6344-
my $func = shift(@functions);
6345-
my $first = $func->line();
6346-
my $end = $func->end_line();
6347-
while ($first < $currentLine) {
6348-
if (@lines) {
6349-
$currentLine = shift @lines;
6350-
} else {
6351-
if (!defined($end)) {
6352-
my $suffix =
6353-
lcovutil::explain_once('derive_end_line',
6354-
" See lcovrc man entry for 'derive_function_end_line'."
6355-
);
6356-
lcovutil::ignorable_error(
6357-
$lcovutil::ERROR_INCONSISTENT_DATA,
6358-
"\"$name\":$first: function " . $func->name() .
6359-
" found on line but no corresponding 'line' coverage data point. Cannot derive function end line."
6360-
. $suffix);
6361-
}
6362-
next FUNC;
6363-
}
6364-
}
6365-
if (!defined($end)) {
6366-
# where is the next function? Find the last 'line' coverpoint
6367-
# less than the start line of that function..
6368-
if (@lines) {
6369-
# if there are no more lines in this file - then everything
6370-
# must be ending on the last line we saw
6371-
if (@functions) {
6372-
my $next_func = $functions[0];
6373-
my $start = $next_func->line();
6374-
while (@lines &&
6375-
$lines[0] < $start) {
6376-
$currentLine = shift @lines;
6377-
}
6378-
} else {
6379-
# last line in the file must be the last line
6380-
# of this function
6381-
if (@lines) {
6382-
$currentLine = $lines[-1];
6383-
} else {
6384-
my $suffix = lcovutil::explain_once('derive_end_line',
6385-
" See lcovrc man entry for 'derive_function_end_line'."
6386-
:
6387-
);
6388-
lcovutil::ignorable_error(
6389-
$lcovutil::ERROR_INCONSISTENT_DATA,
6390-
"\"$name\":$first: function " .
6391-
$func->name() .
6392-
": last line in file is not last line of function.$suffix"
6393-
);
6394-
next FUNC;
6395-
}
6396-
}
6397-
} elsif ($currentLine < $first) {
6398-
# we ran out of lines in the data...check for inconsistency
6399-
my $suffix =
6400-
lcovutil::explain_once('derive_end_line',
6401-
" See lcovrc man entry for 'derive_function_end_line'.");
6402-
lcovutil::ignorable_error(
6403-
$lcovutil::ERROR_INCONSISTENT_DATA,
6404-
"\"$name\":$first: function " . $func->name() .
6405-
" found on line but no corresponding 'line' coverage data point. Cannot derive function end line."
6406-
. $suffix);
6407-
6408-
# last FUNC; # quit looking here - all the other functions after this one will have same issue
6409-
next FUNC; # warn about them all
6410-
}
6411-
lcovutil::info(1,
6412-
"\"$name\":$currentLine: assign end_line " .
6413-
$func->name() . "\n");
6414-
$func->set_end_line($currentLine);
6415-
}
6416-
# we may not have set the end line above due to inconsistency
6417-
# but we also might not have line data
6418-
# - see .../tests/lcov/extract with gcc/4.8
6419-
if (!defined($func->end_line())) {
6420-
my $suffix =
6421-
lcovutil::explain_once('derive_end_line',
6422-
" See lcovrc man entry for 'derive_function_end_line'.");
6423-
6424-
lcovutil::ignorable_error(
6425-
$lcovutil::ERROR_INCONSISTENT_DATA,
6426-
'"' . $func->filename() . '":' . $func->line() .
6427-
': failed to set end line for function ' .
6428-
$func->name() . '.' . $suffix);
6429-
next FUNC;
6430-
}
6431-
6432-
# now look for this function in each testcase -
6433-
# set the same endline (if not already set)
6434-
my $key = $func->file() . ':' . $first;
6435-
foreach my $tn ($funcData->keylist()) {
6436-
my $d = $funcData->value($tn);
6437-
my $f = $d->findKey($key);
6438-
if (defined($f)) {
6439-
if (!defined($f->end_line())) {
6440-
$f->set_end_line($func->end_line());
6441-
} else {
6442-
if ($f->end_line() != $func->end_line()) {
6443-
lcovutil::ignorable_error(
6444-
$lcovutil::ERROR_INCONSISTENT_DATA,
6445-
'"' . $func->file() .
6446-
'":' . $first . ': function \'' .
6447-
$func->name() . ' last line is ' .
6448-
$func->end_line() . ' but is ' .
6449-
$f->end_line() . " in testcase '$tn'"
6450-
);
6451-
}
6452-
}
6453-
}
6454-
} #foreach testcase
6455-
} # for each function
6479+
# try to derive end lines if at least one is unknown.
6480+
# can't compute for lambdas because we can't distinguish
6481+
# the last line reliably.
6482+
$actions = DID_DERIVE
6483+
if grep({ !($_->isLambda() || defined($_->end_line())) }
6484+
$traceInfo->func()->valuelist());
64566485
}
64576486

64586487
# munge the source file name, if requested
64596488
#die("unexpected path substitution for '$source_file': '" .
64606489
# lcovutil::subst_file_name($source_file) . "'")
64616490
# unless ($source_file eq lcovutil::subst_file_name($source_file));
64626491

6463-
next
6464-
unless (
6465-
(defined($lcovutil::func_coverage) &&
6466-
(0 != scalar(@lcovutil::exclude_function_patterns) ||
6467-
defined($lcovutil::cov_filter[$FILTER_TRIVIAL_FUNCTION]))
6468-
) ||
6469-
(is_language('c|perl|python|java', $source_file) &&
6470-
lcovutil::is_filter_enabled()));
6471-
push(@filter_workList, [$traceInfo, $name]);
6492+
if ((defined($lcovutil::func_coverage) &&
6493+
(0 != scalar(@lcovutil::exclude_function_patterns) ||
6494+
defined($lcovutil::cov_filter[$FILTER_TRIVIAL_FUNCTION]))) ||
6495+
(is_language('c|perl|python|java', $source_file) &&
6496+
lcovutil::is_filter_enabled())
6497+
) {
6498+
# we are forking anyway - so also compute end lines there
6499+
$actions |= DID_FILTER;
6500+
push(@filter_workList, [$traceInfo, $name, $actions]);
6501+
} elsif (0 != $actions) {
6502+
# all we are doing is deriving function end lines - which doesn't
6503+
# take long enough to be worth forking
6504+
TraceFile::_deriveFunctionEndLines($traceInfo);
6505+
}
64726506
} # foreach file
64736507
$self->[STATE] |= DID_DERIVE;
64746508

@@ -7353,9 +7387,10 @@ sub merge
73537387
++$idx;
73547388
}
73557389
lcovutil::info("Using " .
7356-
scalar(@segments) . ' segment' . (scalar(@segments) > 1 ? 's' : '') .
7357-
" of $testsPerSegment test" . ($testsPerSegment > 1 ? 's' : '') .
7358-
"\n");
7390+
scalar(@segments) .
7391+
' segment' . (scalar(@segments) > 1 ? 's' : '') .
7392+
" of $testsPerSegment test" .
7393+
($testsPerSegment > 1 ? 's' : '') . "\n");
73597394
$lcovutil::profileData{config} = {}
73607395
unless exists($lcovutil::profileData{config});
73617396
$lcovutil::profileData{config}{segments} = scalar(@segments);

0 commit comments

Comments
 (0)