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
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ check:
mkdir -p $(COVER_DB) ; \
echo "*** Run once, force parallel ***" ; \
LCOV_FORCE_PARALLEL=1 $(MAKE) -s -C tests check LCOV_HOME=`pwd` ; \
LCOV_FORCE_PARALLEL=1 $(MAKE) -s -C example LCOV_HOME=`pwd` ; \
echo "*** Run again, no force ***" ; \
fi
@$(MAKE) -s -C tests check LCOV_HOME=`pwd`
Expand All @@ -293,7 +294,7 @@ else
@echo "Checking changes in source files for coding style issues (MODE=diff):"
endif
@RC=0 ; \
CHECKFILES=`find . -path ./.git -prune -o \( \( -type f -exec grep -q '^#!.*perl' {} \; \) -o -name '*.pm' \) -not \( -name '*.tdy' -o -name '*.orig' -o -name '*~' \) -print `; \
CHECKFILES=`find bin lib scripts tests -path ./.git -prune -o \( \( -type f -exec grep -q '^#!.*perl' {} \; \) -o -name '*.pm' \) -not \( -name '*.tdy' -o -name '*.orig' -o -name '*~' \) -print `; \
for FILE in $$CHECKFILES ; do \
$(CHECKSTYLE) "$$FILE"; \
if [ 0 != $$? ] ; then \
Expand Down
9 changes: 6 additions & 3 deletions README
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
-------------------------------------------------
- README file for the LTP GCOV extension (LCOV) -
- Last changes: 2025-10-01
- Last changes: 2026-02-10
-------------------------------------------------

Description
Expand Down Expand Up @@ -430,6 +430,9 @@ LCOV features and capabilities fall into 7 major categories:
vii) compress the 'function detail' table to improve readability
by shortening long C++ template and function names.
Sample script: simplify.pm
viii) use history from previous execution to improve runtime
performance of current execution by improving load balancing.
Sample script: history.pm

The callback may be any desired script or executable - but there
may be performance advantages if it is written as a Perl module.
Expand All @@ -445,7 +448,7 @@ LCOV features and capabilities fall into 7 major categories:
Related options:
--annotate-script, --criteria-script, --version-script
--resolve-script, --select-script, --context-script
--simplify-script
--simplify-script --history-script

f) Performance

Expand All @@ -466,7 +469,7 @@ LCOV features and capabilities fall into 7 major categories:

See the genhtml/lcov/geninfo man pages for details

Related options: --parallel, --memory, --profile
Related options: --parallel, --memory, --profile, --history-script

g) Language/tool support

Expand Down
567 changes: 402 additions & 165 deletions bin/genhtml

Large diffs are not rendered by default.

71 changes: 55 additions & 16 deletions bin/geninfo
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ our $defaultChunkSize;
our $defaultInterval;
our %childRetryCounts;
our @large_files;
our $callFromLcov;

our $cwd = getcwd();
chomp($cwd);
Expand All @@ -243,6 +244,7 @@ $ENV{"LC_ALL"} = "C";

# retrieve settings from RC file - use these if not overridden on command line
my %geninfo_opts = ("test-name|t=s" => \$test_name,
'call-from-lcov' => \$callFromLcov,
"output-filename|o=s" => \$output_filename,
"base-directory|b=s" => \$base_directory,
"follow|f" => \$lcovutil::opt_follow,
Expand Down Expand Up @@ -906,15 +908,15 @@ sub add_worklist_entry
{
my ($self, $filename, $directory) = @_;
if (exists($self->[1]->{$filename})) {
lcovutil::ignorable_error(
$lcovutil::ERROR_USAGE,
"duplicate file $filename in both " .
$self->[1]->{$filename} . " and $directory"
.
(lcovutil::is_ignored(
lcovutil::ignorable_error($lcovutil::ERROR_USAGE,
"duplicate file $filename in both " .
$self->[1]->{$filename} .
" and $directory"
.
(lcovutil::is_ignored(
$lcovutil::ERROR_USAGE) ?
' (skip latter)' :
''));
' (skip latter)' :
''));
return;
}
$self->[1]->{$filename} = $directory;
Expand Down Expand Up @@ -1333,8 +1335,45 @@ sub gen_info(@)
}
my @worklist;
my $serialChunk = [1, []];
my $chunk = [0, []]; # [isSerial, [fileList]]
FILE: foreach my $j (@$filelist) {
my $nChunks =
int(floor((scalar(@$filelist) + $chunkSize - 1) / $chunkSize));
for (my $i = 0; $i < $nChunks; ++$i) {
push(@worklist, [0, []]);
}
my $work = $filelist; # default to the original order
if ($lcovutil::profileHistoryCallback &&
($chunkSize > 1 || exists($ENV{LCOV_FORCE_PARALLEL}))) {
my (@predicted, @unknown);
foreach my $j (@$filelist) {
my ($dir, $gcda, $gcno) = @$j;
my $key = defined($gcda) ? $gcda : $gcno;
my $k = substr($key, length($dir) + 1);
my $predicted = $lcovutil::profileHistoryCallback->history($k);
if (defined($predicted)) {
push(@predicted, [$j, $predicted, $key]);
} else {
# put the ones we don't know about at the start of the list
# - work on them first, trusting that we will have parallelism
# capacity for the ones we know about
push(@unknown, $j);
}
}
@predicted =
sort({ $b->[1] <=> $a->[1] or $a->[2] cmp $b->[2] } @predicted);

lcovutil::info(scalar(@unknown) .
' (of ' . (scalar(@predicted) + scalar(@unknown)) .
") files do not appear in \'--history\' profile.\n")
if (@unknown);
if (@predicted) {
my @order = map({ $_->[0] } @predicted);
# unknown to the back...
push(@order, @unknown);
$work = \@order;
}
}
my $chunkId = 0;
FILE: foreach my $j (@$work) {
my ($dir, $gcda, $gcno) = @$j;
foreach my $f ($gcda, $gcno) {
next unless defined($f); # might not be a GCDA file
Expand All @@ -1345,15 +1384,15 @@ sub gen_info(@)
next FILE;
}
}
die("unexpected chunkId $chunkId") if $chunkId >= $nChunks;
my $chunk = $worklist[$chunkId++];
push(@{$chunk->[1]}, $j);
if (scalar(@{$chunk->[1]}) == $chunkSize) {
push(@worklist, $chunk);
$chunk = [0, []];
if ($chunkId == $nChunks) {
$chunkId = 0;
}
} #foreach DATA_FILE
push(@worklist, $chunk) if @{$chunk->[1]};
# serial chunk is at the top of the stack - so serial processing
# happens before we fork multiple processes
# serial chunk is at the top of the stack - so serial processing
# happens before we fork multiple processes
push(@worklist, $serialChunk)
if (@{$serialChunk->[1]});

Expand Down
2 changes: 1 addition & 1 deletion bin/get_version.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ else
fi

# Fallback
[ -z "$VERSION" ] && VERSION="2.4"
[ -z "$VERSION" ] && VERSION="2.4.1"
[ -z "$RELEASE" ] && RELEASE="beta"
[ -z "$FULL" ] && FULL="$VERSION-$RELEASE"

Expand Down
7 changes: 7 additions & 0 deletions bin/lcov
Original file line number Diff line number Diff line change
Expand Up @@ -821,6 +821,7 @@ sub lcov_geninfo(@)
@param = (File::Spec->catfile($tool_dir, 'geninfo'), @dir);
# make things less confusing for user, by using the name they actually invoked
push(@param, '--toolname', $lcovutil::tool_name);
push(@param, '--call-from-lcov');
if ($output_filename) {
push(@param, "--output-filename", $output_filename);
}
Expand Down Expand Up @@ -914,6 +915,9 @@ sub lcov_geninfo(@)
# memory has not been multiplied by Mb yet - so just pass the integer value
push(@param, '--memory', $lcovutil::maxMemory)
if defined($lcovutil::maxMemory);
# function coverage was enabled by default
push(@param, '--no-function-coverage')
if (!$lcovutil::func_coverage);
push(@param, "--branch-coverage") if $lcovutil::br_coverage;
push(@param, "--mcdc") if $lcovutil::mcdc_coverage;
push(@param, '--fail-under-lines', $lcovutil::fail_under_lines)
Expand All @@ -935,6 +939,9 @@ sub lcov_geninfo(@)
['--version-script', \@lcovutil::extractVersionScript],
['--resolve-script', \@lcovutil::resolveCallback],
['--substitute', \@lcovutil::file_subst_patterns],
['--history-script',
\@lcovutil::profileHistoryCallback
],
['--omit-lines', \@lcovutil::omit_line_patterns],
['--erase-functions',
\@lcovutil::exclude_function_patterns
Expand Down
26 changes: 18 additions & 8 deletions example/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ gauss.o: methods/gauss.c gauss.h
output: example descriptions test_noargs test_2_to_2000 test_overflow test_differential
@echo
@echo '*'
@echo '* Generating HTML output'
@echo '* Generating HTML output from data captured from tests'
@echo '*'
@echo
$(GENHTML) trace_noargs.info trace_args.info trace_overflow.info \
Expand All @@ -95,7 +95,7 @@ output: example descriptions test_noargs test_2_to_2000 test_overflow test_diffe
@echo '* See '`pwd`/output/index.html
@echo '*'
@echo
@echo "Generate HTML with hierarchical report and additional navigation features"
@echo "Generate HTML with hierarchical report and additional navigation features (same testcase data)"
@echo '*'
$(GENHTML) trace_noargs.info trace_args.info trace_overflow.info \
--output-directory hierarchical \
Expand Down Expand Up @@ -175,7 +175,8 @@ test_differential:

@echo "Step 2: Capture initial coverage"
(cd $(REPO) ; \
$(LCOV) --capture -o baseline.info -d . --version-script $(SCRIPTDIR)/gitversion.pm )
$(LCOV) --capture -o baseline.info -d . \
--version-script $(SCRIPTDIR)/gitversion.pm --profile )

@echo "Step 3: Modify source code"
@(cd $(REPO) ; \
Expand All @@ -192,27 +193,34 @@ test_differential:
./example )

@echo "Step 5: Capture new coverage (after source changes)"
@echo " (note that this example is too tiny for history reuse"
@echo " to affect runtime performance)"
(cd $(REPO) ; \
$(LCOV) --capture -o current.info -d . --version-script $(SCRIPTDIR)/gitversion.pm )
$(LCOV) --capture -o current.info -d . \
--version-script $(SCRIPTDIR)/gitversion.pm \
--profile \
--history $(SCRIPTDIR)/history.pm,baseline.info.json )
@echo "Compute source diffs"
(cd $(REPO) ; \
$(SCRIPTDIR)/gitdiff -b . `git rev-list -n 1 baseline` \
`git rev-list -n 1 current` > udiff.txt )

@echo "Step 6: Generate differential coverage report"
@echo " (caching revision control data may result in improved runtime performance)"
@echo " - caching revision control data may result in improved runtime performance)"
@echo " - capture 'genhtml' execution profile that we can use later"
(cd $(REPO) ; \
$(GENHTML) -o differential --baseline-file baseline.info \
--diff-file udiff.txt --show-owners \
--title "Differential coverage example" \
--annotate $(SCRIPTDIR)/gitblame.pm,--cache,./my_cache \
--version-script $(SCRIPTDIR)/gitversion.pm \
--version-script $(SCRIPTDIR)/gitversion.pm --profile \
-- current.info )

@echo "point your browser to `realpath $(REPO)`/differential/index.html"

@echo "Step 7: Generate subset report for code review:"
@echo " (reuse revision control data cached in previous step)"
@echo " - reuse execution profile and cached revision control data"
@echo " captured in previous step"
(cd $(REPO) ; \
$(GENHTML) -o review --baseline-file baseline.info \
--diff-file udiff.txt --show-owners \
Expand All @@ -221,7 +229,9 @@ test_differential:
--version-script $(SCRIPTDIR)/gitversion.pm \
--select-script $(SCRIPTDIR)/select.pm \
--select-script --tla --select-script UNC,UIC,LBC \
-- current.info )
--history-script $(SCRIPTDIR)/history.pm \
--history-script ./differential/genhtml.json --profile \
-- current.info )

@echo "point your browser to `realpath $(REPO)`/review/index.html"

Expand Down
11 changes: 11 additions & 0 deletions example/README
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,16 @@ Code review:
- Real use cases are likely to use more sophisticated select-script
callbacks (e.g., to select from a range of changelists).

- The example uses caching and profile history to improve runtime
performance - see the man pages for a more detailed description
of the features. There is no effect with a tiny example - but
a real project may see benefit.
The 'spreadsheet.py' application script can be used to convert
JSON profile files into more readable excel spreadsheets. This
can be useful to see the effect (if any) of the caching and/or
history features, and can show where time is spent for your example.
This can be helpful, to suggest opportunities to optimize the LCOV
implementation.

Feel free to edit the Makefile or to run the lcov utilities directly,
to see the effect of other options that you find in the lcov man pages.
Loading