Skip to content

Commit

Permalink
Bugfix: ignore lambdas during function end line derivation -
Browse files Browse the repository at this point in the history
they would need to be found syntactically.
Recommend user to upgrade to newer compiler.

Signed-off-by: Henry Cox <[email protected]>
  • Loading branch information
henry2cox committed Jan 2, 2024
1 parent cbe14e8 commit c2fd3ab
Show file tree
Hide file tree
Showing 7 changed files with 262 additions and 6 deletions.
20 changes: 19 additions & 1 deletion lib/lcovutil.pm
Original file line number Diff line number Diff line change
Expand Up @@ -3441,12 +3441,25 @@ sub name
return $self->[NAME];
}

sub filename
{
my $self = shift;
return $self->[FILE];
}

sub hit
{
my $self = shift;
return $self->[COUNT];
}

sub isLambda
{
my $self = shift;
return (TraceFile::is_c_file($self->filename()) &&
$self->name() =~ /{lambda\(/);
}

sub count
{
my ($self, $alias, $merged) = @_;
Expand Down Expand Up @@ -6168,8 +6181,10 @@ sub applyFilters
is_c_file($source_file)) {
my @lines = sort { $a <=> $b } $traceInfo->sum()->keylist();
# sort functions by start line number
# ignore lambdas - which we don't process correctly at the moment
# (would need to do syntactic search for the end line)
my @functions = sort { $a->line() <=> $b->line() }
$traceInfo->func()->valuelist();
grep({ !$_->isLambda() } $traceInfo->func()->valuelist());

my $currentLine = @lines ? shift(@lines) : 0;
my $funcData = $traceInfo->testfnc();
Expand Down Expand Up @@ -6235,6 +6250,9 @@ sub applyFilters
$func->name() . "\n");
$func->set_end_line($currentLine);
}
die('failed to set end line for ' .
$func->name() . ' in file ' . $func->filename())
unless defined($func->end_line());
# now look for this function in each testcase -
# set the same endline (if not already set)
my $key = $func->file() . ':' . $first;
Expand Down
10 changes: 6 additions & 4 deletions man/genhtml.1
Original file line number Diff line number Diff line change
Expand Up @@ -1748,7 +1748,7 @@ In the 'function coverage detail' table, also show the percentage of lines and b
This feature enables developers to focus attention on functions which have the largest effect on overall code coverage.

This feature is disabled by default.
Note that this option requires that you use a gcc version which is new enough to support function begin/end line reports or that you configure the tool to derive the required dta - see the
Note that this option requires that you use a compiler version which is new enough to support function begin/end line reports or that you configure the tool to derive the required data - see the
.BI derive_function_end_line
discussion in man
.B lcovrc(5).
Expand Down Expand Up @@ -1784,7 +1784,7 @@ Exclude coverage data from lines which fall within a function whose name matches
.B \-\-demangle\-cpp
option is used or not.

Note that this option requires that you use a gcc version which is new enough to support function begin/end line reports or that you configure the tool to derive the required dta - see the
Note that this option requires that you use a compiler version which is new enough to support function begin/end line reports or that you configure the tool to derive the required data - see the
.BI derive_function_end_line
discussion in man
.B lcovrc(5).
Expand Down Expand Up @@ -2280,8 +2280,10 @@ a link to a page which lists all functions found in that file plus the
respective call count for those functions.
The function coverage page groups the data for every alias of each function, sorted by name or execution count. The representative name of the group of functions is the shorted (i.e., containing the fewest characters).

If using differential coverage and a sufficiently recent gcc version which report both begin and end line of functions (gcc/9 and newer), functions are considered 'new' if any of their source lines have changed.
With older gcc versions, functions are considered 'new' if the function signature has changed or if the entire function is new.
If using differential coverage and a sufficiently recent compiler version which report both begin and end line of functions (
.I e.g.,
gcc/9 and newer), functions are considered 'new' if any of their source lines have changed.
With older compiler versions, functions are considered 'new' if the function signature has changed or if the entire function is new.

.RE
.B \-\-branch\-coverage
Expand Down
6 changes: 6 additions & 0 deletions man/lcovrc.5
Original file line number Diff line number Diff line change
Expand Up @@ -1309,6 +1309,12 @@ Note that end lines are derived only for C/C++ files; see the
.I c_file_extensions
setting, above, for the list of extensions used to identify these files.
.br
Lambda functions are ignored during end line computation. Note that lambdas
are identified via function name matching - so you must enable demangling
if your toolchain is too old to report demangled names in the GCOV output.
See the
.I demangle_cpp
setting, above.

Default is 1.
.PP
Expand Down
2 changes: 1 addition & 1 deletion tests/genhtml/Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
include ../common.mak

TESTS := full.sh part1.sh part2.sh target.sh zero.sh demangle.sh relative/
TESTS := full.sh part1.sh part2.sh target.sh zero.sh demangle.sh relative lambda

clean:
rm -rf *.log out_* *.tmp
6 changes: 6 additions & 0 deletions tests/genhtml/lambda/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
include ../../common.mak

TESTS := lambda.sh

clean:
$(shell ./lambda.sh --clean)
83 changes: 83 additions & 0 deletions tests/genhtml/lambda/lambda.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// borrowed from https://en.cppreference.com/w/cpp/utility/functional/bind
#include <functional>
#include <iostream>
#include <memory>
#include <random>

void f(int n1, int n2, int n3, const int& n4, int n5)
{
std::cout << n1 << ' ' << n2 << ' ' << n3 << ' ' << n4 << ' ' << n5 << '\n';
}

int g(int n1)
{
return n1;
}

struct Foo
{
void print_sum(int n1, int n2)
{
std::cout << n1 + n2 << '\n';
}

int data = 10;
};

int main()
{
using namespace std::placeholders; // for _1, _2, _3...

std::cout << "1) argument reordering and pass-by-reference: ";
int n = 7;
// (_1 and _2 are from std::placeholders, and represent future
// arguments that will be passed to f1)
auto f1 = std::bind(f, _2, 42, _1, std::cref(n), n);
n = 10;
f1(1, 2, 1001); // 1 is bound by _1, 2 is bound by _2, 1001 is unused
// makes a call to f(2, 42, 1, n, 7)

std::cout << "2) achieving the same effect using a lambda: ";
n = 7;
auto lambda = [&ncref = n, n](auto a, auto b, auto /*unused*/)
{
f(b, 42, a, ncref, n);
};
n = 10;
lambda(1, 2, 1001); // same as a call to f1(1, 2, 1001)

std::cout << "3) nested bind subexpressions share the placeholders: ";
auto f2 = std::bind(f, _3, std::bind(g, _3), _3, 4, 5);
f2(10, 11, 12); // makes a call to f(12, g(12), 12, 4, 5);

std::cout << "4) bind a RNG with a distribution: ";
std::default_random_engine e;
std::uniform_int_distribution<> d(0, 10);
auto rnd = std::bind(d, e); // a copy of e is stored in rnd
for (int n = 0; n < 10; ++n)
std::cout << rnd() << ' ';
std::cout << '\n';

std::cout << "5) bind to a pointer to member function: ";
Foo foo;
auto f3 = std::bind(&Foo::print_sum, &foo, 95, _1);
f3(5);

std::cout << "6) bind to a mem_fn that is a pointer to member function: ";
auto ptr_to_print_sum = std::mem_fn(&Foo::print_sum);
auto f4 = std::bind(ptr_to_print_sum, &foo, 95, _1);
f4(5);

std::cout << "7) bind to a pointer to data member: ";
auto f5 = std::bind(&Foo::data, _1);
std::cout << f5(foo) << '\n';

std::cout << "8) bind to a mem_fn that is a pointer to data member: ";
auto ptr_to_data = std::mem_fn(&Foo::data);
auto f6 = std::bind(ptr_to_data, _1);
std::cout << f6(foo) << '\n';

std::cout << "9) use smart pointers to call members of the referenced objects: ";
std::cout << f6(std::make_shared<Foo>(foo)) << ' '
<< f6(std::make_unique<Foo>(foo)) << '\n';
}
141 changes: 141 additions & 0 deletions tests/genhtml/lambda/lambda.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
#!/bin/bash

# lambda funciton extents
set +x

CLEAN_ONLY=0
COVER=

PARALLEL='--parallel 0'
PROFILE="--profile"
COVER_DB='cover_db'
LOCAL_COVERAGE=1
KEEP_GOING=0
while [ $# -gt 0 ] ; do

OPT=$1
shift
case $OPT in

--clean | clean )
CLEAN_ONLY=1
;;

-v | --verbose | verbose )
set -x
;;

--keep-going )
KEEP_GOING=1
;;

--coverage )
#COVER="perl -MDevel::Cover "
if [[ "$1"x != 'x' && $1 != "-"* ]] ; then
COVER_DB=$1
LOCAL_COVERAGE=0
shift
fi
COVER="perl -MDevel::Cover=-db,$COVER_DB,-coverage,statement,branch,condition,subroutine "
;;

--home | -home )
LCOV_HOME=$1
shift
if [ ! -f $LCOV_HOME/bin/lcov ] ; then
echo "LCOV_HOME '$LCOV_HOME' does not exist"
exit 1
fi
;;

--no-parallel )
PARALLEL=''
;;

--no-profile )
PROFILE=''
;;

* )
echo "Error: unexpected option '$OPT'"
exit 1
;;
esac
done

if [[ "x" == ${LCOV_HOME}x ]] ; then
if [ -f ../../../bin/lcov ] ; then
LCOV_HOME=../../..
else
LCOV_HOME=../../../../releng/coverage/lcov
fi
fi
LCOV_HOME=`(cd ${LCOV_HOME} ; pwd)`

if [[ ! ( -d $LCOV_HOME/bin && -d $LCOV_HOME/lib && -x $LCOV_HOME/bin/genhtml && ( -f $LCOV_HOME/lib/lcovutil.pm || -f $LCOV_HOME/lib/lcov/lcovutil.pm ) ) ]] ; then
echo "LCOV_HOME '$LCOV_HOME' seems not to be invalid"
exit 1
fi

export PATH=${LCOV_HOME}/bin:${LCOV_HOME}/share:${PATH}
export MANPATH=${MANPATH}:${LCOV_HOME}/man

ROOT=`pwd`
PARENT=`(cd .. ; pwd)`

LCOV_OPTS="--branch $PARALLEL $PROFILE"
# gcc/4.8.5 (and possibly other old versions) generate inconsistent line/function data
IFS='.' read -r -a VER <<< `gcc -dumpversion`
if [ "${VER[0]}" -lt 5 ] ; then
IGNORE="--ignore inconsistent"
# and filter exception branches to avoid spurious differences for old compiler
FILTER='--filter branch'
fi
rm -rf *.txt* *.json dumper* report lambda *.gcda *.gcno *.info
if [ "x$COVER" != 'x' ] && [ 0 != $LOCAL_COVERAGE ] ; then
cover -delete
fi
if [[ 1 == $CLEAN_ONLY ]] ; then
exit 0
fi
if ! type g++ >/dev/null 2>&1 ; then
echo "Missing tool: g++" >&2
exit 2
fi
g++ -o lambda --coverage lambda.cpp -std=c++1y
./lambda
if [ 0 != $? ] ; then
echo "Error: 'lambda' returned error code"
if [ $KEEP_GOING == 0 ] ; then
exit 1
fi
fi
$COVER $LCOV_HOME/bin/lcov $LCOV_OPTS -o lambda.info --capture -d . --demangle --rc derive_function_end_line=0
if [ 0 != $? ] ; then
echo "Error: unexpected error code from lcov"
if [ $KEEP_GOING == 0 ] ; then
exit 1
fi
fi
$COVER $LCOV_HOME/bin/genhtml $LCOV_OPTS -o report lambda.info --show-proportion
if [ 0 != $? ] ; then
echo "Error: unexpected error code from genhtml"
if [ $KEEP_GOING == 0 ] ; then
exit 1
fi
fi
echo "Tests passed"
if [ "x$COVER" != "x" ] && [ $LOCAL_COVERAGE == 1 ]; then
cover
fi

0 comments on commit c2fd3ab

Please sign in to comment.