| From ec3e1f411c332cbc2f2bc7ab7e2175ebf918b37a Mon Sep 17 00:00:00 2001 |
| From: Peter Oberparleiter <oberpar@linux.ibm.com> |
| Date: Fri, 24 May 2019 16:56:52 +0200 |
| Subject: [PATCH 1/2] geninfo: Add intermediate text format support |
| |
| This change adds support for parsing the output of gcov's intermediate |
| text file format as implemented by GCC versions 5 to 8. The use of the |
| gcov intermediate format should increase processing speed. It also |
| provides branch coverage data when using the --initial command line |
| option. |
| |
| Users can control whether geninfo uses the intermediate format via the |
| geninfo_intermediate configuration file option. Valid values are: |
| |
| 0: Use normal text format |
| 1: Use intermediate format |
| auto: Use intermediate format if available. This is the default. |
| |
| Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com> |
| --- |
| bin/geninfo | 567 ++++++++++++++++++++++++++++++++++++++++++++------- |
| lcovrc | 3 + |
| man/lcovrc.5 | 24 +++ |
| 3 files changed, 521 insertions(+), 73 deletions(-) |
| |
| Upstream-Status: Backport |
| Download URL: https://github.com/linux-test-project/lcov/commit/ebfeb3e179e450c69c3532f98cd5ea1fbf6ccba7 |
| |
| diff --git a/bin/geninfo b/bin/geninfo |
| index f41eaec..0276666 100755 |
| --- a/bin/geninfo |
| +++ b/bin/geninfo |
| @@ -54,6 +54,8 @@ use warnings; |
| use File::Basename; |
| use File::Spec::Functions qw /abs2rel catdir file_name_is_absolute splitdir |
| splitpath catpath/; |
| +use File::Temp qw(tempfile tempdir); |
| +use File::Copy qw(copy); |
| use Getopt::Long; |
| use Digest::MD5 qw(md5_base64); |
| use Cwd qw/abs_path/; |
| @@ -163,13 +165,13 @@ sub solve_relative_path($$); |
| sub read_gcov_header($); |
| sub read_gcov_file($); |
| sub info(@); |
| +sub process_intermediate($$$); |
| sub map_llvm_version($); |
| sub version_to_str($); |
| sub get_gcov_version(); |
| sub system_no_output($@); |
| sub read_config($); |
| sub apply_config($); |
| -sub get_exclusion_data($); |
| sub apply_exclusion_data($$); |
| sub process_graphfile($$); |
| sub filter_fn_name($); |
| @@ -264,6 +266,8 @@ our $gcno_split_crc; |
| our $func_coverage = 1; |
| our $br_coverage = 0; |
| our $rc_auto_base = 1; |
| +our $rc_intermediate = "auto"; |
| +our $intermediate; |
| our $excl_line = "LCOV_EXCL_LINE"; |
| our $excl_br_line = "LCOV_EXCL_BR_LINE"; |
| |
| @@ -331,6 +335,7 @@ if ($config || %opt_rc) |
| "geninfo_compat" => \$opt_compat, |
| "geninfo_adjust_src_path" => \$rc_adjust_src_path, |
| "geninfo_auto_base" => \$rc_auto_base, |
| + "geninfo_intermediate" => \$rc_intermediate, |
| "lcov_function_coverage" => \$func_coverage, |
| "lcov_branch_coverage" => \$br_coverage, |
| "lcov_excl_line" => \$excl_line, |
| @@ -460,15 +465,38 @@ if (system_no_output(3, $gcov_tool, "--help") == -1) |
| } |
| |
| ($gcov_version, $gcov_version_string) = get_gcov_version(); |
| +$gcov_caps = get_gcov_capabilities(); |
| + |
| +# Determine intermediate mode |
| +if ($rc_intermediate eq "0") { |
| + $intermediate = 0; |
| +} elsif ($rc_intermediate eq "1") { |
| + $intermediate = 1; |
| +} elsif (lc($rc_intermediate) eq "auto") { |
| + # Use intermediate format if supported by gcov |
| + $intermediate = $gcov_caps->{'intermediate-format'} ? 1 : 0; |
| +} else { |
| + die("ERROR: invalid value for geninfo_intermediate: ". |
| + "'$rc_intermediate'\n"); |
| +} |
| + |
| +if ($intermediate) { |
| + info("Using intermediate gcov format\n"); |
| + if ($opt_derive_func_data) { |
| + warn("WARNING: --derive-func-data is not compatible with ". |
| + "intermediate format - ignoring\n"); |
| + $opt_derive_func_data = 0; |
| + } |
| +} |
| |
| # Determine gcov options |
| -$gcov_caps = get_gcov_capabilities(); |
| push(@gcov_options, "-b") if ($gcov_caps->{'branch-probabilities'} && |
| ($br_coverage || $func_coverage)); |
| push(@gcov_options, "-c") if ($gcov_caps->{'branch-counts'} && |
| $br_coverage); |
| push(@gcov_options, "-a") if ($gcov_caps->{'all-blocks'} && |
| - $opt_gcov_all_blocks && $br_coverage); |
| + $opt_gcov_all_blocks && $br_coverage && |
| + !$intermediate); |
| if ($gcov_caps->{'hash-filenames'}) |
| { |
| push(@gcov_options, "-x"); |
| @@ -599,7 +627,7 @@ foreach my $entry (@data_directory) { |
| gen_info($entry); |
| } |
| |
| -if ($initial && $br_coverage) { |
| +if ($initial && $br_coverage && !$intermediate) { |
| warn("Note: --initial does not generate branch coverage ". |
| "data\n"); |
| } |
| @@ -768,6 +796,7 @@ sub gen_info($) |
| my $prefix; |
| my $type; |
| my $ext; |
| + my $tempdir; |
| |
| if ($initial) { |
| $type = "graph"; |
| @@ -798,16 +827,22 @@ sub gen_info($) |
| $prefix = ""; |
| } |
| |
| + $tempdir = tempdir(CLEANUP => 1); |
| + |
| # Process all files in list |
| foreach $file (@file_list) { |
| # Process file |
| - if ($initial) { |
| + if ($intermediate) { |
| + process_intermediate($file, $prefix, $tempdir); |
| + } elsif ($initial) { |
| process_graphfile($file, $prefix); |
| } else { |
| process_dafile($file, $prefix); |
| } |
| } |
| |
| + unlink($tempdir); |
| + |
| # Report whether files were excluded. |
| if (%excluded_files) { |
| info("Excluded data for %d files due to include/exclude options\n", |
| @@ -1058,10 +1093,12 @@ sub process_dafile($$) |
| |
| # Try to find base directory automatically if requested by user |
| if ($rc_auto_base) { |
| - $base_dir = find_base_from_graph($base_dir, $instr, $graph); |
| + $base_dir = find_base_from_source($base_dir, |
| + [ keys(%{$instr}), keys(%{$graph}) ]); |
| } |
| |
| - ($instr, $graph) = adjust_graph_filenames($base_dir, $instr, $graph); |
| + adjust_source_filenames($instr, $base_dir); |
| + adjust_source_filenames($graph, $base_dir); |
| |
| # Set $object_dir to real location of object files. This may differ |
| # from $da_dir if the graph file is just a link to the "real" object |
| @@ -2017,6 +2054,299 @@ sub read_gcov_file($) |
| } |
| |
| |
| +# |
| +# read_intermediate_text(gcov_filename, data) |
| +# |
| +# Read gcov intermediate text format in GCOV_FILENAME and add the resulting |
| +# data to DATA in the following format: |
| +# |
| +# data: source_filename -> file_data |
| +# file_data: concatenated lines of intermediate text data |
| +# |
| + |
| +sub read_intermediate_text($$) |
| +{ |
| + my ($gcov_filename, $data) = @_; |
| + my $fd; |
| + my $filename; |
| + |
| + open($fd, "<", $gcov_filename) or |
| + die("ERROR: Could not read $gcov_filename: $!\n"); |
| + while (my $line = <$fd>) { |
| + if ($line =~ /^file:(.*)$/) { |
| + $filename = $1; |
| + chomp($filename); |
| + } elsif (defined($filename)) { |
| + $data->{$filename} .= $line; |
| + } |
| + } |
| + close($fd); |
| +} |
| + |
| + |
| +# |
| +# intermediate_text_to_info(fd, data, srcdata) |
| +# |
| +# Write DATA in info format to file descriptor FD. |
| +# |
| +# data: filename -> file_data: |
| +# file_data: concatenated lines of intermediate text data |
| +# |
| +# srcdata: filename -> [ excl, brexcl, checksums ] |
| +# excl: lineno -> 1 for all lines for which to exclude all data |
| +# brexcl: lineno -> 1 for all lines for which to exclude branch data |
| +# checksums: lineno -> source code checksum |
| +# |
| +# Note: To simplify processing, gcov data is not combined here, that is counts |
| +# that appear multiple times for the same lines/branches are not added. |
| +# This is done by lcov/genhtml when reading the data files. |
| +# |
| + |
| +sub intermediate_text_to_info($$$) |
| +{ |
| + my ($fd, $data, $srcdata) = @_; |
| + my $branch_num = 0; |
| + my $c; |
| + |
| + return if (!%{$data}); |
| + |
| + print($fd "TN:$test_name\n"); |
| + for my $filename (keys(%{$data})) { |
| + my ($excl, $brexcl, $checksums); |
| + |
| + if (defined($srcdata->{$filename})) { |
| + ($excl, $brexcl, $checksums) = @{$srcdata->{$filename}}; |
| + } |
| + |
| + print($fd "SF:$filename\n"); |
| + for my $line (split(/\n/, $data->{$filename})) { |
| + if ($line =~ /^lcount:(\d+),(\d+),?/) { |
| + # lcount:<line>,<count> |
| + # lcount:<line>,<count>,<has_unexecuted_blocks> |
| + if ($checksum && exists($checksums->{$1})) { |
| + $c = ",".$checksums->{$1}; |
| + } else { |
| + $c = ""; |
| + } |
| + print($fd "DA:$1,$2$c\n") if (!$excl->{$1}); |
| + |
| + # Intermediate text format does not provide |
| + # branch numbers, and the same branch may appear |
| + # multiple times on the same line (e.g. in |
| + # template instances). Synthesize a branch |
| + # number based on the assumptions: |
| + # a) the order of branches is fixed across |
| + # instances |
| + # b) an instance starts with an lcount line |
| + $branch_num = 0; |
| + } elsif ($line =~ /^function:(\d+),(\d+),([^,]+)$/) { |
| + next if (!$func_coverage || $excl->{$1}); |
| + |
| + # function:<line>,<count>,<name> |
| + print($fd "FN:$1,$3\n"); |
| + print($fd "FNDA:$2,$3\n"); |
| + } elsif ($line =~ /^function:(\d+),\d+,(\d+),([^,]+)$/) { |
| + next if (!$func_coverage || $excl->{$1}); |
| + |
| + # function:<start_line>,<end_line>,<count>, |
| + # <name> |
| + print($fd "FN:$1,$3\n"); |
| + print($fd "FNDA:$2,$3\n"); |
| + } elsif ($line =~ /^branch:(\d+),(taken|nottaken|notexec)/) { |
| + next if (!$br_coverage || $excl->{$1} || |
| + $brexcl->{$1}); |
| + |
| + # branch:<line>,taken|nottaken|notexec |
| + if ($2 eq "taken") { |
| + $c = 1; |
| + } elsif ($2 eq "nottaken") { |
| + $c = 0; |
| + } else { |
| + $c = "-"; |
| + } |
| + print($fd "BRDA:$1,0,$branch_num,$c\n"); |
| + $branch_num++; |
| + } |
| + } |
| + print($fd "end_of_record\n"); |
| + } |
| +} |
| + |
| + |
| +sub get_output_fd($$) |
| +{ |
| + my ($outfile, $file) = @_; |
| + my $fd; |
| + |
| + if (!defined($outfile)) { |
| + open($fd, ">", "$file.info") or |
| + die("ERROR: Cannot create file $file.info: $!\n"); |
| + } elsif ($outfile eq "-") { |
| + open($fd, ">&STDOUT") or |
| + die("ERROR: Cannot duplicate stdout: $!\n"); |
| + } else { |
| + open($fd, ">>", $outfile) or |
| + die("ERROR: Cannot write to file $outfile: $!\n"); |
| + } |
| + |
| + return $fd; |
| +} |
| + |
| + |
| +# |
| +# print_gcov_warnings(stderr_file, is_graph, map) |
| +# |
| +# Print GCOV warnings in file STDERR_FILE to STDERR. If IS_GRAPH is non-zero, |
| +# suppress warnings about missing as these are expected. Replace keys found |
| +# in MAP with their values. |
| +# |
| + |
| +sub print_gcov_warnings($$$) |
| +{ |
| + my ($stderr_file, $is_graph, $map) = @_; |
| + my $fd; |
| + |
| + if (!open($fd, "<", $stderr_file)) { |
| + warn("WARNING: Could not open GCOV stderr file ". |
| + "$stderr_file: $!\n"); |
| + return; |
| + } |
| + while (my $line = <$fd>) { |
| + next if ($is_graph && $line =~ /cannot open data file/); |
| + |
| + for my $key (keys(%{$map})) { |
| + $line =~ s/\Q$key\E/$map->{$key}/g; |
| + } |
| + |
| + print(STDERR $line); |
| + } |
| + close($fd); |
| +} |
| + |
| + |
| +# |
| +# process_intermediate(file, dir, tempdir) |
| +# |
| +# Create output for a single file (either a data file or a graph file) using |
| +# gcov's intermediate option. |
| +# |
| + |
| +sub process_intermediate($$$) |
| +{ |
| + my ($file, $dir, $tempdir) = @_; |
| + my ($fdir, $fbase, $fext); |
| + my $data_file; |
| + my $errmsg; |
| + my %data; |
| + my $fd; |
| + my $base; |
| + my $srcdata; |
| + my $is_graph = 0; |
| + my ($out, $err, $rc); |
| + |
| + info("Processing %s\n", abs2rel($file, $dir)); |
| + |
| + $file = solve_relative_path($cwd, $file); |
| + ($fdir, $fbase, $fext) = split_filename($file); |
| + |
| + $is_graph = 1 if (".$fext" eq $graph_file_extension); |
| + |
| + if ($is_graph) { |
| + # Process graph file - copy to temp directory to prevent |
| + # accidental processing of associated data file |
| + $data_file = "$tempdir/$fbase$graph_file_extension"; |
| + if (!copy($file, $data_file)) { |
| + $errmsg = "ERROR: Could not copy file $file"; |
| + goto err; |
| + } |
| + } else { |
| + # Process data file in place |
| + $data_file = $file; |
| + } |
| + |
| + # Change directory |
| + if (!chdir($tempdir)) { |
| + $errmsg = "Could not change to directory $tempdir: $!"; |
| + goto err; |
| + } |
| + |
| + # Run gcov on data file |
| + ($out, $err, $rc) = system_no_output(1 + 2 + 4, $gcov_tool, |
| + $data_file, @gcov_options, "-i"); |
| + defined($out) && unlink($out); |
| + if (defined($err)) { |
| + print_gcov_warnings($err, $is_graph, { |
| + $data_file => $file, |
| + }); |
| + unlink($err); |
| + } |
| + if ($rc) { |
| + $errmsg = "GCOV failed for $file"; |
| + goto err; |
| + } |
| + |
| + if ($is_graph) { |
| + # Remove graph file copy |
| + unlink($data_file); |
| + } |
| + |
| + # Parse resulting file(s) |
| + for my $gcov_filename (glob("*.gcov")) { |
| + read_intermediate_text($gcov_filename, \%data); |
| + unlink($gcov_filename); |
| + } |
| + |
| + if (!%data) { |
| + warn("WARNING: GCOV did not produce any data for $file\n"); |
| + return; |
| + } |
| + |
| + # Determine base directory |
| + if (defined($base_directory)) { |
| + $base = $base_directory; |
| + } else { |
| + $base = $fdir; |
| + |
| + if (is_compat($COMPAT_MODE_LIBTOOL)) { |
| + # Avoid files from .libs dirs |
| + $base =~ s/\.libs$//; |
| + } |
| + |
| + # Try to find base directory automatically if requested by user |
| + if ($rc_auto_base) { |
| + $base = find_base_from_source($base, [ keys(%data) ]); |
| + } |
| + } |
| + |
| + # Apply base file name to relative source files |
| + adjust_source_filenames(\%data, $base); |
| + |
| + # Remove excluded source files |
| + filter_source_files(\%data); |
| + |
| + # Get data on exclusion markers and checksums if requested |
| + if (!$no_markers || $checksum) { |
| + $srcdata = get_all_source_data(keys(%data)); |
| + } |
| + |
| + # Generate output |
| + $fd = get_output_fd($output_filename, $file); |
| + intermediate_text_to_info($fd, \%data, $srcdata); |
| + close($fd); |
| + |
| + chdir($cwd); |
| + |
| + return; |
| + |
| +err: |
| + if ($ignore[$ERROR_GCOV]) { |
| + warn("WARNING: $errmsg!\n"); |
| + } else { |
| + die("ERROR: $errmsg!\n") |
| + } |
| +} |
| + |
| + |
| # Map LLVM versions to the version of GCC gcov which they emulate. |
| |
| sub map_llvm_version($) |
| @@ -2151,8 +2481,12 @@ sub int_handler() |
| # |
| # MODE & 1: suppress STDOUT |
| # MODE & 2: suppress STDERR |
| +# MODE & 4: redirect to temporary files instead of suppressing |
| # |
| -# Return 0 on success, non-zero otherwise. |
| +# Return (stdout, stderr, rc): |
| +# stdout: path to tempfile containing stdout or undef |
| +# stderr: path to tempfile containing stderr or undef |
| +# 0 on success, non-zero otherwise |
| # |
| |
| sub system_no_output($@) |
| @@ -2161,14 +2495,31 @@ sub system_no_output($@) |
| my $result; |
| local *OLD_STDERR; |
| local *OLD_STDOUT; |
| + my $stdout_file; |
| + my $stderr_file; |
| + my $fd; |
| |
| # Save old stdout and stderr handles |
| ($mode & 1) && open(OLD_STDOUT, ">>&", "STDOUT"); |
| ($mode & 2) && open(OLD_STDERR, ">>&", "STDERR"); |
| |
| - # Redirect to /dev/null |
| - ($mode & 1) && open(STDOUT, ">", "/dev/null"); |
| - ($mode & 2) && open(STDERR, ">", "/dev/null"); |
| + if ($mode & 4) { |
| + # Redirect to temporary files |
| + if ($mode & 1) { |
| + ($fd, $stdout_file) = tempfile(UNLINK => 1); |
| + open(STDOUT, ">", $stdout_file) || warn("$!\n"); |
| + close($fd); |
| + } |
| + if ($mode & 2) { |
| + ($fd, $stderr_file) = tempfile(UNLINK => 1); |
| + open(STDERR, ">", $stderr_file) || warn("$!\n"); |
| + close($fd); |
| + } |
| + } else { |
| + # Redirect to /dev/null |
| + ($mode & 1) && open(STDOUT, ">", "/dev/null"); |
| + ($mode & 2) && open(STDERR, ">", "/dev/null"); |
| + } |
| |
| debug("system(".join(' ', @_).")\n"); |
| system(@_); |
| @@ -2181,8 +2532,18 @@ sub system_no_output($@) |
| # Restore old handles |
| ($mode & 1) && open(STDOUT, ">>&", "OLD_STDOUT"); |
| ($mode & 2) && open(STDERR, ">>&", "OLD_STDERR"); |
| + |
| + # Remove empty output files |
| + if (defined($stdout_file) && -z $stdout_file) { |
| + unlink($stdout_file); |
| + $stdout_file = undef; |
| + } |
| + if (defined($stderr_file) && -z $stderr_file) { |
| + unlink($stderr_file); |
| + $stderr_file = undef; |
| + } |
| |
| - return $result; |
| + return ($stdout_file, $stderr_file, $result); |
| } |
| |
| |
| @@ -2260,23 +2621,28 @@ sub apply_config($) |
| |
| |
| # |
| -# get_exclusion_data(filename) |
| +# get_source_data(filename) |
| # |
| -# Scan specified source code file for exclusion markers and return |
| -# linenumber -> 1 |
| -# for all lines which should be excluded. |
| +# Scan specified source code file for exclusion markers and checksums. Return |
| +# ( excl, brexcl, checksums ) where |
| +# excl: lineno -> 1 for all lines for which to exclude all data |
| +# brexcl: lineno -> 1 for all lines for which to exclude branch data |
| +# checksums: lineno -> source code checksum |
| # |
| |
| -sub get_exclusion_data($) |
| +sub get_source_data($) |
| { |
| my ($filename) = @_; |
| my %list; |
| my $flag = 0; |
| + my %brdata; |
| + my $brflag = 0; |
| + my %checksums; |
| local *HANDLE; |
| |
| if (!open(HANDLE, "<", $filename)) { |
| warn("WARNING: could not open $filename\n"); |
| - return undef; |
| + return; |
| } |
| while (<HANDLE>) { |
| if (/$EXCL_STOP/) { |
| @@ -2287,14 +2653,62 @@ sub get_exclusion_data($) |
| if (/$excl_line/ || $flag) { |
| $list{$.} = 1; |
| } |
| + if (/$EXCL_BR_STOP/) { |
| + $brflag = 0; |
| + } elsif (/$EXCL_BR_START/) { |
| + $brflag = 1; |
| + } |
| + if (/$excl_br_line/ || $brflag) { |
| + $brdata{$.} = 1; |
| + } |
| + if ($checksum) { |
| + chomp(); |
| + $checksums{$.} = md5_base64($_); |
| + } |
| } |
| close(HANDLE); |
| |
| - if ($flag) { |
| + if ($flag || $brflag) { |
| warn("WARNING: unterminated exclusion section in $filename\n"); |
| } |
| |
| - return \%list; |
| + return (\%list, \%brdata, \%checksums); |
| +} |
| + |
| + |
| +# |
| +# get_all_source_data(filenames) |
| +# |
| +# Scan specified source code files for exclusion markers and return |
| +# filename -> [ excl, brexcl, checksums ] |
| +# excl: lineno -> 1 for all lines for which to exclude all data |
| +# brexcl: lineno -> 1 for all lines for which to exclude branch data |
| +# checksums: lineno -> source code checksum |
| +# |
| + |
| +sub get_all_source_data(@) |
| +{ |
| + my @filenames = @_; |
| + my %data; |
| + my $failed = 0; |
| + |
| + for my $filename (@filenames) { |
| + my @d; |
| + next if (exists($data{$filename})); |
| + |
| + @d = get_source_data($filename); |
| + if (@d) { |
| + $data{$filename} = [ @d ]; |
| + } else { |
| + $failed = 1; |
| + } |
| + } |
| + |
| + if ($failed) { |
| + warn("WARNING: some exclusion markers may be ignored\n"); |
| + } |
| + |
| + return \%data; |
| } |
| |
| |
| @@ -2318,35 +2732,17 @@ sub apply_exclusion_data($$) |
| { |
| my ($instr, $graph) = @_; |
| my $filename; |
| - my %excl_data; |
| - my $excl_read_failed = 0; |
| + my $excl_data; |
| |
| - # Collect exclusion marker data |
| - foreach $filename (sort_uniq_lex(keys(%{$graph}), keys(%{$instr}))) { |
| - my $excl = get_exclusion_data($filename); |
| - |
| - # Skip and note if file could not be read |
| - if (!defined($excl)) { |
| - $excl_read_failed = 1; |
| - next; |
| - } |
| - |
| - # Add to collection if there are markers |
| - $excl_data{$filename} = $excl if (keys(%{$excl}) > 0); |
| - } |
| - |
| - # Warn if not all source files could be read |
| - if ($excl_read_failed) { |
| - warn("WARNING: some exclusion markers may be ignored\n"); |
| - } |
| + ($excl_data) = get_all_source_data(keys(%{$graph}), keys(%{$instr})); |
| |
| # Skip if no markers were found |
| - return ($instr, $graph) if (keys(%excl_data) == 0); |
| + return ($instr, $graph) if (!%$excl_data); |
| |
| # Apply exclusion marker data to graph |
| - foreach $filename (keys(%excl_data)) { |
| + foreach $filename (keys(%$excl_data)) { |
| my $function_data = $graph->{$filename}; |
| - my $excl = $excl_data{$filename}; |
| + my $excl = $excl_data->{$filename}->[0]; |
| my $function; |
| |
| next if (!defined($function_data)); |
| @@ -2384,9 +2780,9 @@ sub apply_exclusion_data($$) |
| } |
| |
| # Apply exclusion marker data to instr |
| - foreach $filename (keys(%excl_data)) { |
| + foreach $filename (keys(%$excl_data)) { |
| my $line_data = $instr->{$filename}; |
| - my $excl = $excl_data{$filename}; |
| + my $excl = $excl_data->{$filename}->[0]; |
| my $line; |
| my @new_data; |
| |
| @@ -2468,10 +2864,12 @@ sub process_graphfile($$) |
| |
| # Try to find base directory automatically if requested by user |
| if ($rc_auto_base) { |
| - $base_dir = find_base_from_graph($base_dir, $instr, $graph); |
| + $base_dir = find_base_from_source($base_dir, |
| + [ keys(%{$instr}), keys(%{$graph}) ]); |
| } |
| |
| - ($instr, $graph) = adjust_graph_filenames($base_dir, $instr, $graph); |
| + adjust_source_filenames($instr, $base_dir); |
| + adjust_source_filenames($graph, $base_dir); |
| |
| if (!$no_markers) { |
| # Apply exclusion marker data to graph file data |
| @@ -2767,11 +3165,11 @@ sub parent_dir($) |
| } |
| |
| # |
| -# find_base_from_graph(base_dir, instr, graph) |
| +# find_base_from_source(base_dir, source_files) |
| # |
| -# Try to determine the base directory of the graph file specified by INSTR |
| -# and GRAPH. The base directory is the base for all relative filenames in |
| -# the graph file. It is defined by the current working directory at time |
| +# Try to determine the base directory of the object file built from |
| +# SOURCE_FILES. The base directory is the base for all relative filenames in |
| +# the gcov data. It is defined by the current working directory at time |
| # of compiling the source file. |
| # |
| # This function implements a heuristic which relies on the following |
| @@ -2781,16 +3179,16 @@ sub parent_dir($) |
| # - files by the same name are not present in multiple parent directories |
| # |
| |
| -sub find_base_from_graph($$$) |
| +sub find_base_from_source($$) |
| { |
| - my ($base_dir, $instr, $graph) = @_; |
| + my ($base_dir, $source_files) = @_; |
| my $old_base; |
| my $best_miss; |
| my $best_base; |
| my %rel_files; |
| |
| # Determine list of relative paths |
| - foreach my $filename (keys(%{$instr}), keys(%{$graph})) { |
| + foreach my $filename (@$source_files) { |
| next if (file_name_is_absolute($filename)); |
| |
| $rel_files{$filename} = 1; |
| @@ -2829,17 +3227,17 @@ sub find_base_from_graph($$$) |
| } |
| |
| # |
| -# adjust_graph_filenames(base_dir, instr, graph) |
| +# adjust_source_filenames(hash, base_dir) |
| # |
| -# Make relative paths in INSTR and GRAPH absolute and apply |
| -# geninfo_adjust_src_path setting to graph file data. |
| +# Transform all keys of HASH to absolute form and apply requested |
| +# transformations. |
| # |
| |
| -sub adjust_graph_filenames($$$) |
| +sub adjust_source_filenames($$$) |
| { |
| - my ($base_dir, $instr, $graph) = @_; |
| + my ($hash, $base_dir) = @_; |
| |
| - foreach my $filename (keys(%{$instr})) { |
| + foreach my $filename (keys(%{$hash})) { |
| my $old_filename = $filename; |
| |
| # Convert to absolute canonical form |
| @@ -2851,28 +3249,50 @@ sub adjust_graph_filenames($$$) |
| } |
| |
| if ($filename ne $old_filename) { |
| - $instr->{$filename} = delete($instr->{$old_filename}); |
| + $hash->{$filename} = delete($hash->{$old_filename}); |
| } |
| } |
| +} |
| |
| - foreach my $filename (keys(%{$graph})) { |
| - my $old_filename = $filename; |
| |
| - # Make absolute |
| - # Convert to absolute canonical form |
| - $filename = solve_relative_path($base_dir, $filename); |
| +# |
| +# filter_source_files(hash) |
| +# |
| +# Remove unwanted source file data from HASH. |
| +# |
| |
| - # Apply adjustment |
| - if (defined($adjust_src_pattern)) { |
| - $filename =~ s/$adjust_src_pattern/$adjust_src_replace/g; |
| +sub filter_source_files($) |
| +{ |
| + my ($hash) = @_; |
| + |
| + foreach my $filename (keys(%{$hash})) { |
| + # Skip external files if requested |
| + goto del if (!$opt_external && is_external($filename)); |
| + |
| + # Apply include patterns |
| + if (@include_patterns) { |
| + my $keep; |
| + |
| + foreach my $pattern (@include_patterns) { |
| + if ($filename =~ (/^$pattern$/)) { |
| + $keep = 1; |
| + last; |
| + } |
| + } |
| + goto del if (!$keep); |
| } |
| |
| - if ($filename ne $old_filename) { |
| - $graph->{$filename} = delete($graph->{$old_filename}); |
| + # Apply exclude patterns |
| + foreach my $pattern (@exclude_patterns) { |
| + goto del if ($filename =~ (/^$pattern$/)); |
| } |
| - } |
| + next; |
| |
| - return ($instr, $graph); |
| +del: |
| + # Remove file data |
| + delete($hash->{$filename}); |
| + $excluded_files{$filename} = 1; |
| + } |
| } |
| |
| # |
| @@ -3784,6 +4204,7 @@ sub get_gcov_capabilities() |
| 'c' => 'branch-counts', |
| 'f' => 'function-summaries', |
| 'h' => 'help', |
| + 'i' => 'intermediate-format', |
| 'l' => 'long-file-names', |
| 'n' => 'no-output', |
| 'o' => 'object-directory', |
| diff --git a/lcovrc b/lcovrc |
| index 40f364f..bd4bc3b 100644 |
| --- a/lcovrc |
| +++ b/lcovrc |
| @@ -134,6 +134,9 @@ genhtml_desc_html=0 |
| # when collecting coverage data. |
| geninfo_auto_base = 1 |
| |
| +# Use gcov intermediate format? Valid values are 0, 1, auto |
| +geninfo_intermediate = auto |
| + |
| # Directory containing gcov kernel files |
| # lcov_gcov_dir = /proc/gcov |
| |
| diff --git a/man/lcovrc.5 b/man/lcovrc.5 |
| index f20d273..bf0ce7a 100644 |
| --- a/man/lcovrc.5 |
| +++ b/man/lcovrc.5 |
| @@ -223,6 +223,11 @@ geninfo_compat_libtool = 0 |
| geninfo_auto_base = 1 |
| .br |
| |
| +# Use gcov intermediate format? Valid values are 0, 1, auto |
| +.br |
| +geninfo_intermediate = auto |
| +.br |
| + |
| # Directory containing gcov kernel files |
| .br |
| lcov_gcov_dir = /proc/gcov |
| @@ -789,6 +794,25 @@ located, and in addition, is different between files of the same project. |
| Default is 1. |
| .PP |
| |
| +.BR geninfo_intermediate " =" |
| +.IR 0 | 1 | auto |
| +.IP |
| +Specify whether to use gcov intermediate format |
| +.br |
| + |
| +Use this option to control whether geninfo should use the gcov intermediate |
| +format while collecting coverage data. The use of the gcov intermediate format |
| +should increase processing speed. It also provides branch coverage data when |
| +using the \-\-initial command line option. |
| +.br |
| + |
| +Valid values are 0 for off, 1 for on, and "auto" to let geninfo automatically |
| +use immediate format when supported by gcov. |
| +.br |
| + |
| +Default is "auto". |
| +.PP |
| + |
| .BR lcov_gcov_dir " =" |
| .I path_to_kernel_coverage_data |
| .IP |
| -- |
| 2.17.1 |
| |