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