1From ec3e1f411c332cbc2f2bc7ab7e2175ebf918b37a Mon Sep 17 00:00:00 2001 2From: Peter Oberparleiter <oberpar@linux.ibm.com> 3Date: Fri, 24 May 2019 16:56:52 +0200 4Subject: [PATCH 1/2] geninfo: Add intermediate text format support 5 6This change adds support for parsing the output of gcov's intermediate 7text file format as implemented by GCC versions 5 to 8. The use of the 8gcov intermediate format should increase processing speed. It also 9provides branch coverage data when using the --initial command line 10option. 11 12Users can control whether geninfo uses the intermediate format via the 13geninfo_intermediate configuration file option. Valid values are: 14 15 0: Use normal text format 16 1: Use intermediate format 17 auto: Use intermediate format if available. This is the default. 18 19Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com> 20--- 21 bin/geninfo | 567 ++++++++++++++++++++++++++++++++++++++++++++------- 22 lcovrc | 3 + 23 man/lcovrc.5 | 24 +++ 24 3 files changed, 521 insertions(+), 73 deletions(-) 25 26Upstream-Status: Backport 27Download URL: https://github.com/linux-test-project/lcov/commit/ebfeb3e179e450c69c3532f98cd5ea1fbf6ccba7 28 29diff --git a/bin/geninfo b/bin/geninfo 30index f41eaec..0276666 100755 31--- a/bin/geninfo 32+++ b/bin/geninfo 33@@ -54,6 +54,8 @@ use warnings; 34 use File::Basename; 35 use File::Spec::Functions qw /abs2rel catdir file_name_is_absolute splitdir 36 splitpath catpath/; 37+use File::Temp qw(tempfile tempdir); 38+use File::Copy qw(copy); 39 use Getopt::Long; 40 use Digest::MD5 qw(md5_base64); 41 use Cwd qw/abs_path/; 42@@ -163,13 +165,13 @@ sub solve_relative_path($$); 43 sub read_gcov_header($); 44 sub read_gcov_file($); 45 sub info(@); 46+sub process_intermediate($$$); 47 sub map_llvm_version($); 48 sub version_to_str($); 49 sub get_gcov_version(); 50 sub system_no_output($@); 51 sub read_config($); 52 sub apply_config($); 53-sub get_exclusion_data($); 54 sub apply_exclusion_data($$); 55 sub process_graphfile($$); 56 sub filter_fn_name($); 57@@ -264,6 +266,8 @@ our $gcno_split_crc; 58 our $func_coverage = 1; 59 our $br_coverage = 0; 60 our $rc_auto_base = 1; 61+our $rc_intermediate = "auto"; 62+our $intermediate; 63 our $excl_line = "LCOV_EXCL_LINE"; 64 our $excl_br_line = "LCOV_EXCL_BR_LINE"; 65 66@@ -331,6 +335,7 @@ if ($config || %opt_rc) 67 "geninfo_compat" => \$opt_compat, 68 "geninfo_adjust_src_path" => \$rc_adjust_src_path, 69 "geninfo_auto_base" => \$rc_auto_base, 70+ "geninfo_intermediate" => \$rc_intermediate, 71 "lcov_function_coverage" => \$func_coverage, 72 "lcov_branch_coverage" => \$br_coverage, 73 "lcov_excl_line" => \$excl_line, 74@@ -460,15 +465,38 @@ if (system_no_output(3, $gcov_tool, "--help") == -1) 75 } 76 77 ($gcov_version, $gcov_version_string) = get_gcov_version(); 78+$gcov_caps = get_gcov_capabilities(); 79+ 80+# Determine intermediate mode 81+if ($rc_intermediate eq "0") { 82+ $intermediate = 0; 83+} elsif ($rc_intermediate eq "1") { 84+ $intermediate = 1; 85+} elsif (lc($rc_intermediate) eq "auto") { 86+ # Use intermediate format if supported by gcov 87+ $intermediate = $gcov_caps->{'intermediate-format'} ? 1 : 0; 88+} else { 89+ die("ERROR: invalid value for geninfo_intermediate: ". 90+ "'$rc_intermediate'\n"); 91+} 92+ 93+if ($intermediate) { 94+ info("Using intermediate gcov format\n"); 95+ if ($opt_derive_func_data) { 96+ warn("WARNING: --derive-func-data is not compatible with ". 97+ "intermediate format - ignoring\n"); 98+ $opt_derive_func_data = 0; 99+ } 100+} 101 102 # Determine gcov options 103-$gcov_caps = get_gcov_capabilities(); 104 push(@gcov_options, "-b") if ($gcov_caps->{'branch-probabilities'} && 105 ($br_coverage || $func_coverage)); 106 push(@gcov_options, "-c") if ($gcov_caps->{'branch-counts'} && 107 $br_coverage); 108 push(@gcov_options, "-a") if ($gcov_caps->{'all-blocks'} && 109- $opt_gcov_all_blocks && $br_coverage); 110+ $opt_gcov_all_blocks && $br_coverage && 111+ !$intermediate); 112 if ($gcov_caps->{'hash-filenames'}) 113 { 114 push(@gcov_options, "-x"); 115@@ -599,7 +627,7 @@ foreach my $entry (@data_directory) { 116 gen_info($entry); 117 } 118 119-if ($initial && $br_coverage) { 120+if ($initial && $br_coverage && !$intermediate) { 121 warn("Note: --initial does not generate branch coverage ". 122 "data\n"); 123 } 124@@ -768,6 +796,7 @@ sub gen_info($) 125 my $prefix; 126 my $type; 127 my $ext; 128+ my $tempdir; 129 130 if ($initial) { 131 $type = "graph"; 132@@ -798,16 +827,22 @@ sub gen_info($) 133 $prefix = ""; 134 } 135 136+ $tempdir = tempdir(CLEANUP => 1); 137+ 138 # Process all files in list 139 foreach $file (@file_list) { 140 # Process file 141- if ($initial) { 142+ if ($intermediate) { 143+ process_intermediate($file, $prefix, $tempdir); 144+ } elsif ($initial) { 145 process_graphfile($file, $prefix); 146 } else { 147 process_dafile($file, $prefix); 148 } 149 } 150 151+ unlink($tempdir); 152+ 153 # Report whether files were excluded. 154 if (%excluded_files) { 155 info("Excluded data for %d files due to include/exclude options\n", 156@@ -1058,10 +1093,12 @@ sub process_dafile($$) 157 158 # Try to find base directory automatically if requested by user 159 if ($rc_auto_base) { 160- $base_dir = find_base_from_graph($base_dir, $instr, $graph); 161+ $base_dir = find_base_from_source($base_dir, 162+ [ keys(%{$instr}), keys(%{$graph}) ]); 163 } 164 165- ($instr, $graph) = adjust_graph_filenames($base_dir, $instr, $graph); 166+ adjust_source_filenames($instr, $base_dir); 167+ adjust_source_filenames($graph, $base_dir); 168 169 # Set $object_dir to real location of object files. This may differ 170 # from $da_dir if the graph file is just a link to the "real" object 171@@ -2017,6 +2054,299 @@ sub read_gcov_file($) 172 } 173 174 175+# 176+# read_intermediate_text(gcov_filename, data) 177+# 178+# Read gcov intermediate text format in GCOV_FILENAME and add the resulting 179+# data to DATA in the following format: 180+# 181+# data: source_filename -> file_data 182+# file_data: concatenated lines of intermediate text data 183+# 184+ 185+sub read_intermediate_text($$) 186+{ 187+ my ($gcov_filename, $data) = @_; 188+ my $fd; 189+ my $filename; 190+ 191+ open($fd, "<", $gcov_filename) or 192+ die("ERROR: Could not read $gcov_filename: $!\n"); 193+ while (my $line = <$fd>) { 194+ if ($line =~ /^file:(.*)$/) { 195+ $filename = $1; 196+ chomp($filename); 197+ } elsif (defined($filename)) { 198+ $data->{$filename} .= $line; 199+ } 200+ } 201+ close($fd); 202+} 203+ 204+ 205+# 206+# intermediate_text_to_info(fd, data, srcdata) 207+# 208+# Write DATA in info format to file descriptor FD. 209+# 210+# data: filename -> file_data: 211+# file_data: concatenated lines of intermediate text data 212+# 213+# srcdata: filename -> [ excl, brexcl, checksums ] 214+# excl: lineno -> 1 for all lines for which to exclude all data 215+# brexcl: lineno -> 1 for all lines for which to exclude branch data 216+# checksums: lineno -> source code checksum 217+# 218+# Note: To simplify processing, gcov data is not combined here, that is counts 219+# that appear multiple times for the same lines/branches are not added. 220+# This is done by lcov/genhtml when reading the data files. 221+# 222+ 223+sub intermediate_text_to_info($$$) 224+{ 225+ my ($fd, $data, $srcdata) = @_; 226+ my $branch_num = 0; 227+ my $c; 228+ 229+ return if (!%{$data}); 230+ 231+ print($fd "TN:$test_name\n"); 232+ for my $filename (keys(%{$data})) { 233+ my ($excl, $brexcl, $checksums); 234+ 235+ if (defined($srcdata->{$filename})) { 236+ ($excl, $brexcl, $checksums) = @{$srcdata->{$filename}}; 237+ } 238+ 239+ print($fd "SF:$filename\n"); 240+ for my $line (split(/\n/, $data->{$filename})) { 241+ if ($line =~ /^lcount:(\d+),(\d+),?/) { 242+ # lcount:<line>,<count> 243+ # lcount:<line>,<count>,<has_unexecuted_blocks> 244+ if ($checksum && exists($checksums->{$1})) { 245+ $c = ",".$checksums->{$1}; 246+ } else { 247+ $c = ""; 248+ } 249+ print($fd "DA:$1,$2$c\n") if (!$excl->{$1}); 250+ 251+ # Intermediate text format does not provide 252+ # branch numbers, and the same branch may appear 253+ # multiple times on the same line (e.g. in 254+ # template instances). Synthesize a branch 255+ # number based on the assumptions: 256+ # a) the order of branches is fixed across 257+ # instances 258+ # b) an instance starts with an lcount line 259+ $branch_num = 0; 260+ } elsif ($line =~ /^function:(\d+),(\d+),([^,]+)$/) { 261+ next if (!$func_coverage || $excl->{$1}); 262+ 263+ # function:<line>,<count>,<name> 264+ print($fd "FN:$1,$3\n"); 265+ print($fd "FNDA:$2,$3\n"); 266+ } elsif ($line =~ /^function:(\d+),\d+,(\d+),([^,]+)$/) { 267+ next if (!$func_coverage || $excl->{$1}); 268+ 269+ # function:<start_line>,<end_line>,<count>, 270+ # <name> 271+ print($fd "FN:$1,$3\n"); 272+ print($fd "FNDA:$2,$3\n"); 273+ } elsif ($line =~ /^branch:(\d+),(taken|nottaken|notexec)/) { 274+ next if (!$br_coverage || $excl->{$1} || 275+ $brexcl->{$1}); 276+ 277+ # branch:<line>,taken|nottaken|notexec 278+ if ($2 eq "taken") { 279+ $c = 1; 280+ } elsif ($2 eq "nottaken") { 281+ $c = 0; 282+ } else { 283+ $c = "-"; 284+ } 285+ print($fd "BRDA:$1,0,$branch_num,$c\n"); 286+ $branch_num++; 287+ } 288+ } 289+ print($fd "end_of_record\n"); 290+ } 291+} 292+ 293+ 294+sub get_output_fd($$) 295+{ 296+ my ($outfile, $file) = @_; 297+ my $fd; 298+ 299+ if (!defined($outfile)) { 300+ open($fd, ">", "$file.info") or 301+ die("ERROR: Cannot create file $file.info: $!\n"); 302+ } elsif ($outfile eq "-") { 303+ open($fd, ">&STDOUT") or 304+ die("ERROR: Cannot duplicate stdout: $!\n"); 305+ } else { 306+ open($fd, ">>", $outfile) or 307+ die("ERROR: Cannot write to file $outfile: $!\n"); 308+ } 309+ 310+ return $fd; 311+} 312+ 313+ 314+# 315+# print_gcov_warnings(stderr_file, is_graph, map) 316+# 317+# Print GCOV warnings in file STDERR_FILE to STDERR. If IS_GRAPH is non-zero, 318+# suppress warnings about missing as these are expected. Replace keys found 319+# in MAP with their values. 320+# 321+ 322+sub print_gcov_warnings($$$) 323+{ 324+ my ($stderr_file, $is_graph, $map) = @_; 325+ my $fd; 326+ 327+ if (!open($fd, "<", $stderr_file)) { 328+ warn("WARNING: Could not open GCOV stderr file ". 329+ "$stderr_file: $!\n"); 330+ return; 331+ } 332+ while (my $line = <$fd>) { 333+ next if ($is_graph && $line =~ /cannot open data file/); 334+ 335+ for my $key (keys(%{$map})) { 336+ $line =~ s/\Q$key\E/$map->{$key}/g; 337+ } 338+ 339+ print(STDERR $line); 340+ } 341+ close($fd); 342+} 343+ 344+ 345+# 346+# process_intermediate(file, dir, tempdir) 347+# 348+# Create output for a single file (either a data file or a graph file) using 349+# gcov's intermediate option. 350+# 351+ 352+sub process_intermediate($$$) 353+{ 354+ my ($file, $dir, $tempdir) = @_; 355+ my ($fdir, $fbase, $fext); 356+ my $data_file; 357+ my $errmsg; 358+ my %data; 359+ my $fd; 360+ my $base; 361+ my $srcdata; 362+ my $is_graph = 0; 363+ my ($out, $err, $rc); 364+ 365+ info("Processing %s\n", abs2rel($file, $dir)); 366+ 367+ $file = solve_relative_path($cwd, $file); 368+ ($fdir, $fbase, $fext) = split_filename($file); 369+ 370+ $is_graph = 1 if (".$fext" eq $graph_file_extension); 371+ 372+ if ($is_graph) { 373+ # Process graph file - copy to temp directory to prevent 374+ # accidental processing of associated data file 375+ $data_file = "$tempdir/$fbase$graph_file_extension"; 376+ if (!copy($file, $data_file)) { 377+ $errmsg = "ERROR: Could not copy file $file"; 378+ goto err; 379+ } 380+ } else { 381+ # Process data file in place 382+ $data_file = $file; 383+ } 384+ 385+ # Change directory 386+ if (!chdir($tempdir)) { 387+ $errmsg = "Could not change to directory $tempdir: $!"; 388+ goto err; 389+ } 390+ 391+ # Run gcov on data file 392+ ($out, $err, $rc) = system_no_output(1 + 2 + 4, $gcov_tool, 393+ $data_file, @gcov_options, "-i"); 394+ defined($out) && unlink($out); 395+ if (defined($err)) { 396+ print_gcov_warnings($err, $is_graph, { 397+ $data_file => $file, 398+ }); 399+ unlink($err); 400+ } 401+ if ($rc) { 402+ $errmsg = "GCOV failed for $file"; 403+ goto err; 404+ } 405+ 406+ if ($is_graph) { 407+ # Remove graph file copy 408+ unlink($data_file); 409+ } 410+ 411+ # Parse resulting file(s) 412+ for my $gcov_filename (glob("*.gcov")) { 413+ read_intermediate_text($gcov_filename, \%data); 414+ unlink($gcov_filename); 415+ } 416+ 417+ if (!%data) { 418+ warn("WARNING: GCOV did not produce any data for $file\n"); 419+ return; 420+ } 421+ 422+ # Determine base directory 423+ if (defined($base_directory)) { 424+ $base = $base_directory; 425+ } else { 426+ $base = $fdir; 427+ 428+ if (is_compat($COMPAT_MODE_LIBTOOL)) { 429+ # Avoid files from .libs dirs 430+ $base =~ s/\.libs$//; 431+ } 432+ 433+ # Try to find base directory automatically if requested by user 434+ if ($rc_auto_base) { 435+ $base = find_base_from_source($base, [ keys(%data) ]); 436+ } 437+ } 438+ 439+ # Apply base file name to relative source files 440+ adjust_source_filenames(\%data, $base); 441+ 442+ # Remove excluded source files 443+ filter_source_files(\%data); 444+ 445+ # Get data on exclusion markers and checksums if requested 446+ if (!$no_markers || $checksum) { 447+ $srcdata = get_all_source_data(keys(%data)); 448+ } 449+ 450+ # Generate output 451+ $fd = get_output_fd($output_filename, $file); 452+ intermediate_text_to_info($fd, \%data, $srcdata); 453+ close($fd); 454+ 455+ chdir($cwd); 456+ 457+ return; 458+ 459+err: 460+ if ($ignore[$ERROR_GCOV]) { 461+ warn("WARNING: $errmsg!\n"); 462+ } else { 463+ die("ERROR: $errmsg!\n") 464+ } 465+} 466+ 467+ 468 # Map LLVM versions to the version of GCC gcov which they emulate. 469 470 sub map_llvm_version($) 471@@ -2151,8 +2481,12 @@ sub int_handler() 472 # 473 # MODE & 1: suppress STDOUT 474 # MODE & 2: suppress STDERR 475+# MODE & 4: redirect to temporary files instead of suppressing 476 # 477-# Return 0 on success, non-zero otherwise. 478+# Return (stdout, stderr, rc): 479+# stdout: path to tempfile containing stdout or undef 480+# stderr: path to tempfile containing stderr or undef 481+# 0 on success, non-zero otherwise 482 # 483 484 sub system_no_output($@) 485@@ -2161,14 +2495,31 @@ sub system_no_output($@) 486 my $result; 487 local *OLD_STDERR; 488 local *OLD_STDOUT; 489+ my $stdout_file; 490+ my $stderr_file; 491+ my $fd; 492 493 # Save old stdout and stderr handles 494 ($mode & 1) && open(OLD_STDOUT, ">>&", "STDOUT"); 495 ($mode & 2) && open(OLD_STDERR, ">>&", "STDERR"); 496 497- # Redirect to /dev/null 498- ($mode & 1) && open(STDOUT, ">", "/dev/null"); 499- ($mode & 2) && open(STDERR, ">", "/dev/null"); 500+ if ($mode & 4) { 501+ # Redirect to temporary files 502+ if ($mode & 1) { 503+ ($fd, $stdout_file) = tempfile(UNLINK => 1); 504+ open(STDOUT, ">", $stdout_file) || warn("$!\n"); 505+ close($fd); 506+ } 507+ if ($mode & 2) { 508+ ($fd, $stderr_file) = tempfile(UNLINK => 1); 509+ open(STDERR, ">", $stderr_file) || warn("$!\n"); 510+ close($fd); 511+ } 512+ } else { 513+ # Redirect to /dev/null 514+ ($mode & 1) && open(STDOUT, ">", "/dev/null"); 515+ ($mode & 2) && open(STDERR, ">", "/dev/null"); 516+ } 517 518 debug("system(".join(' ', @_).")\n"); 519 system(@_); 520@@ -2181,8 +2532,18 @@ sub system_no_output($@) 521 # Restore old handles 522 ($mode & 1) && open(STDOUT, ">>&", "OLD_STDOUT"); 523 ($mode & 2) && open(STDERR, ">>&", "OLD_STDERR"); 524+ 525+ # Remove empty output files 526+ if (defined($stdout_file) && -z $stdout_file) { 527+ unlink($stdout_file); 528+ $stdout_file = undef; 529+ } 530+ if (defined($stderr_file) && -z $stderr_file) { 531+ unlink($stderr_file); 532+ $stderr_file = undef; 533+ } 534 535- return $result; 536+ return ($stdout_file, $stderr_file, $result); 537 } 538 539 540@@ -2260,23 +2621,28 @@ sub apply_config($) 541 542 543 # 544-# get_exclusion_data(filename) 545+# get_source_data(filename) 546 # 547-# Scan specified source code file for exclusion markers and return 548-# linenumber -> 1 549-# for all lines which should be excluded. 550+# Scan specified source code file for exclusion markers and checksums. Return 551+# ( excl, brexcl, checksums ) where 552+# excl: lineno -> 1 for all lines for which to exclude all data 553+# brexcl: lineno -> 1 for all lines for which to exclude branch data 554+# checksums: lineno -> source code checksum 555 # 556 557-sub get_exclusion_data($) 558+sub get_source_data($) 559 { 560 my ($filename) = @_; 561 my %list; 562 my $flag = 0; 563+ my %brdata; 564+ my $brflag = 0; 565+ my %checksums; 566 local *HANDLE; 567 568 if (!open(HANDLE, "<", $filename)) { 569 warn("WARNING: could not open $filename\n"); 570- return undef; 571+ return; 572 } 573 while (<HANDLE>) { 574 if (/$EXCL_STOP/) { 575@@ -2287,14 +2653,62 @@ sub get_exclusion_data($) 576 if (/$excl_line/ || $flag) { 577 $list{$.} = 1; 578 } 579+ if (/$EXCL_BR_STOP/) { 580+ $brflag = 0; 581+ } elsif (/$EXCL_BR_START/) { 582+ $brflag = 1; 583+ } 584+ if (/$excl_br_line/ || $brflag) { 585+ $brdata{$.} = 1; 586+ } 587+ if ($checksum) { 588+ chomp(); 589+ $checksums{$.} = md5_base64($_); 590+ } 591 } 592 close(HANDLE); 593 594- if ($flag) { 595+ if ($flag || $brflag) { 596 warn("WARNING: unterminated exclusion section in $filename\n"); 597 } 598 599- return \%list; 600+ return (\%list, \%brdata, \%checksums); 601+} 602+ 603+ 604+# 605+# get_all_source_data(filenames) 606+# 607+# Scan specified source code files for exclusion markers and return 608+# filename -> [ excl, brexcl, checksums ] 609+# excl: lineno -> 1 for all lines for which to exclude all data 610+# brexcl: lineno -> 1 for all lines for which to exclude branch data 611+# checksums: lineno -> source code checksum 612+# 613+ 614+sub get_all_source_data(@) 615+{ 616+ my @filenames = @_; 617+ my %data; 618+ my $failed = 0; 619+ 620+ for my $filename (@filenames) { 621+ my @d; 622+ next if (exists($data{$filename})); 623+ 624+ @d = get_source_data($filename); 625+ if (@d) { 626+ $data{$filename} = [ @d ]; 627+ } else { 628+ $failed = 1; 629+ } 630+ } 631+ 632+ if ($failed) { 633+ warn("WARNING: some exclusion markers may be ignored\n"); 634+ } 635+ 636+ return \%data; 637 } 638 639 640@@ -2318,35 +2732,17 @@ sub apply_exclusion_data($$) 641 { 642 my ($instr, $graph) = @_; 643 my $filename; 644- my %excl_data; 645- my $excl_read_failed = 0; 646+ my $excl_data; 647 648- # Collect exclusion marker data 649- foreach $filename (sort_uniq_lex(keys(%{$graph}), keys(%{$instr}))) { 650- my $excl = get_exclusion_data($filename); 651- 652- # Skip and note if file could not be read 653- if (!defined($excl)) { 654- $excl_read_failed = 1; 655- next; 656- } 657- 658- # Add to collection if there are markers 659- $excl_data{$filename} = $excl if (keys(%{$excl}) > 0); 660- } 661- 662- # Warn if not all source files could be read 663- if ($excl_read_failed) { 664- warn("WARNING: some exclusion markers may be ignored\n"); 665- } 666+ ($excl_data) = get_all_source_data(keys(%{$graph}), keys(%{$instr})); 667 668 # Skip if no markers were found 669- return ($instr, $graph) if (keys(%excl_data) == 0); 670+ return ($instr, $graph) if (!%$excl_data); 671 672 # Apply exclusion marker data to graph 673- foreach $filename (keys(%excl_data)) { 674+ foreach $filename (keys(%$excl_data)) { 675 my $function_data = $graph->{$filename}; 676- my $excl = $excl_data{$filename}; 677+ my $excl = $excl_data->{$filename}->[0]; 678 my $function; 679 680 next if (!defined($function_data)); 681@@ -2384,9 +2780,9 @@ sub apply_exclusion_data($$) 682 } 683 684 # Apply exclusion marker data to instr 685- foreach $filename (keys(%excl_data)) { 686+ foreach $filename (keys(%$excl_data)) { 687 my $line_data = $instr->{$filename}; 688- my $excl = $excl_data{$filename}; 689+ my $excl = $excl_data->{$filename}->[0]; 690 my $line; 691 my @new_data; 692 693@@ -2468,10 +2864,12 @@ sub process_graphfile($$) 694 695 # Try to find base directory automatically if requested by user 696 if ($rc_auto_base) { 697- $base_dir = find_base_from_graph($base_dir, $instr, $graph); 698+ $base_dir = find_base_from_source($base_dir, 699+ [ keys(%{$instr}), keys(%{$graph}) ]); 700 } 701 702- ($instr, $graph) = adjust_graph_filenames($base_dir, $instr, $graph); 703+ adjust_source_filenames($instr, $base_dir); 704+ adjust_source_filenames($graph, $base_dir); 705 706 if (!$no_markers) { 707 # Apply exclusion marker data to graph file data 708@@ -2767,11 +3165,11 @@ sub parent_dir($) 709 } 710 711 # 712-# find_base_from_graph(base_dir, instr, graph) 713+# find_base_from_source(base_dir, source_files) 714 # 715-# Try to determine the base directory of the graph file specified by INSTR 716-# and GRAPH. The base directory is the base for all relative filenames in 717-# the graph file. It is defined by the current working directory at time 718+# Try to determine the base directory of the object file built from 719+# SOURCE_FILES. The base directory is the base for all relative filenames in 720+# the gcov data. It is defined by the current working directory at time 721 # of compiling the source file. 722 # 723 # This function implements a heuristic which relies on the following 724@@ -2781,16 +3179,16 @@ sub parent_dir($) 725 # - files by the same name are not present in multiple parent directories 726 # 727 728-sub find_base_from_graph($$$) 729+sub find_base_from_source($$) 730 { 731- my ($base_dir, $instr, $graph) = @_; 732+ my ($base_dir, $source_files) = @_; 733 my $old_base; 734 my $best_miss; 735 my $best_base; 736 my %rel_files; 737 738 # Determine list of relative paths 739- foreach my $filename (keys(%{$instr}), keys(%{$graph})) { 740+ foreach my $filename (@$source_files) { 741 next if (file_name_is_absolute($filename)); 742 743 $rel_files{$filename} = 1; 744@@ -2829,17 +3227,17 @@ sub find_base_from_graph($$$) 745 } 746 747 # 748-# adjust_graph_filenames(base_dir, instr, graph) 749+# adjust_source_filenames(hash, base_dir) 750 # 751-# Make relative paths in INSTR and GRAPH absolute and apply 752-# geninfo_adjust_src_path setting to graph file data. 753+# Transform all keys of HASH to absolute form and apply requested 754+# transformations. 755 # 756 757-sub adjust_graph_filenames($$$) 758+sub adjust_source_filenames($$$) 759 { 760- my ($base_dir, $instr, $graph) = @_; 761+ my ($hash, $base_dir) = @_; 762 763- foreach my $filename (keys(%{$instr})) { 764+ foreach my $filename (keys(%{$hash})) { 765 my $old_filename = $filename; 766 767 # Convert to absolute canonical form 768@@ -2851,28 +3249,50 @@ sub adjust_graph_filenames($$$) 769 } 770 771 if ($filename ne $old_filename) { 772- $instr->{$filename} = delete($instr->{$old_filename}); 773+ $hash->{$filename} = delete($hash->{$old_filename}); 774 } 775 } 776+} 777 778- foreach my $filename (keys(%{$graph})) { 779- my $old_filename = $filename; 780 781- # Make absolute 782- # Convert to absolute canonical form 783- $filename = solve_relative_path($base_dir, $filename); 784+# 785+# filter_source_files(hash) 786+# 787+# Remove unwanted source file data from HASH. 788+# 789 790- # Apply adjustment 791- if (defined($adjust_src_pattern)) { 792- $filename =~ s/$adjust_src_pattern/$adjust_src_replace/g; 793+sub filter_source_files($) 794+{ 795+ my ($hash) = @_; 796+ 797+ foreach my $filename (keys(%{$hash})) { 798+ # Skip external files if requested 799+ goto del if (!$opt_external && is_external($filename)); 800+ 801+ # Apply include patterns 802+ if (@include_patterns) { 803+ my $keep; 804+ 805+ foreach my $pattern (@include_patterns) { 806+ if ($filename =~ (/^$pattern$/)) { 807+ $keep = 1; 808+ last; 809+ } 810+ } 811+ goto del if (!$keep); 812 } 813 814- if ($filename ne $old_filename) { 815- $graph->{$filename} = delete($graph->{$old_filename}); 816+ # Apply exclude patterns 817+ foreach my $pattern (@exclude_patterns) { 818+ goto del if ($filename =~ (/^$pattern$/)); 819 } 820- } 821+ next; 822 823- return ($instr, $graph); 824+del: 825+ # Remove file data 826+ delete($hash->{$filename}); 827+ $excluded_files{$filename} = 1; 828+ } 829 } 830 831 # 832@@ -3784,6 +4204,7 @@ sub get_gcov_capabilities() 833 'c' => 'branch-counts', 834 'f' => 'function-summaries', 835 'h' => 'help', 836+ 'i' => 'intermediate-format', 837 'l' => 'long-file-names', 838 'n' => 'no-output', 839 'o' => 'object-directory', 840diff --git a/lcovrc b/lcovrc 841index 40f364f..bd4bc3b 100644 842--- a/lcovrc 843+++ b/lcovrc 844@@ -134,6 +134,9 @@ genhtml_desc_html=0 845 # when collecting coverage data. 846 geninfo_auto_base = 1 847 848+# Use gcov intermediate format? Valid values are 0, 1, auto 849+geninfo_intermediate = auto 850+ 851 # Directory containing gcov kernel files 852 # lcov_gcov_dir = /proc/gcov 853 854diff --git a/man/lcovrc.5 b/man/lcovrc.5 855index f20d273..bf0ce7a 100644 856--- a/man/lcovrc.5 857+++ b/man/lcovrc.5 858@@ -223,6 +223,11 @@ geninfo_compat_libtool = 0 859 geninfo_auto_base = 1 860 .br 861 862+# Use gcov intermediate format? Valid values are 0, 1, auto 863+.br 864+geninfo_intermediate = auto 865+.br 866+ 867 # Directory containing gcov kernel files 868 .br 869 lcov_gcov_dir = /proc/gcov 870@@ -789,6 +794,25 @@ located, and in addition, is different between files of the same project. 871 Default is 1. 872 .PP 873 874+.BR geninfo_intermediate " =" 875+.IR 0 | 1 | auto 876+.IP 877+Specify whether to use gcov intermediate format 878+.br 879+ 880+Use this option to control whether geninfo should use the gcov intermediate 881+format while collecting coverage data. The use of the gcov intermediate format 882+should increase processing speed. It also provides branch coverage data when 883+using the \-\-initial command line option. 884+.br 885+ 886+Valid values are 0 for off, 1 for on, and "auto" to let geninfo automatically 887+use immediate format when supported by gcov. 888+.br 889+ 890+Default is "auto". 891+.PP 892+ 893 .BR lcov_gcov_dir " =" 894 .I path_to_kernel_coverage_data 895 .IP 896-- 8972.17.1 898 899