From e13b2b6f8443da660cafa0679c3b16240843ce9f Mon Sep 17 00:00:00 2001
From: Peter Oberparleiter <oberpar@linux.ibm.com>
Date: Fri, 24 May 2019 17:16:56 +0200
Subject: [PATCH 2/2] geninfo: Add intermediate JSON format support

This change adds support for parsing the output of gcov's intermediate
JSON file format as implemented by GCC version 9.

Note: The way that the intermediate file format support is implemented
in geninfo removes the need to parse .gcno files directly. Since geninfo
does not include support for parsing GCC 9 .gcno files, using the
intermediate format is the only option for geninfo to collect coverage
data generated by GCC version 9.

Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com>
---
 bin/geninfo | 162 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 160 insertions(+), 2 deletions(-)

Upstream-Status: Backport
Download URL: https://github.com/linux-test-project/lcov/commit/75fbae1cfc5027f818a0bb865bf6f96fab3202da

diff --git a/bin/geninfo b/bin/geninfo
index 0276666..cceb782 100755
--- a/bin/geninfo
+++ b/bin/geninfo
@@ -59,6 +59,9 @@ use File::Copy qw(copy);
 use Getopt::Long;
 use Digest::MD5 qw(md5_base64);
 use Cwd qw/abs_path/;
+use PerlIO::gzip;
+use JSON qw(decode_json);
+
 if( $^O eq "msys" )
 {
 	require File::Spec::Win32;
@@ -474,7 +477,8 @@ if ($rc_intermediate eq "0") {
 	$intermediate = 1;
 } elsif (lc($rc_intermediate) eq "auto") {
 	# Use intermediate format if supported by gcov
-	$intermediate = $gcov_caps->{'intermediate-format'} ? 1 : 0;
+	$intermediate = ($gcov_caps->{'intermediate-format'} ||
+			 $gcov_caps->{'json-format'}) ? 1 : 0;
 } else {
 	die("ERROR: invalid value for geninfo_intermediate: ".
 	    "'$rc_intermediate'\n");
@@ -2084,6 +2088,48 @@ sub read_intermediate_text($$)
 }
 
 
+#
+# read_intermediate_json(gcov_filename, data, basedir_ref)
+#
+# Read gcov intermediate JSON format in GCOV_FILENAME and add the resulting
+# data to DATA in the following format:
+#
+# data:      source_filename -> file_data
+# file_data: GCOV JSON data for file
+#
+# Also store the value for current_working_directory to BASEDIR_REF.
+#
+
+sub read_intermediate_json($$$)
+{
+	my ($gcov_filename, $data, $basedir_ref) = @_;
+	my $fd;
+	my $text;
+	my $json;
+
+	open($fd, "<:gzip", $gcov_filename) or
+		die("ERROR: Could not read $gcov_filename: $!\n");
+	local $/;
+	$text = <$fd>;
+	close($fd);
+
+	$json = decode_json($text);
+	if (!defined($json) || !exists($json->{"files"}) ||
+	    ref($json->{"files"} ne "ARRAY")) {
+		die("ERROR: Unrecognized JSON output format in ".
+		    "$gcov_filename\n");
+	}
+
+	$$basedir_ref = $json->{"current_working_directory"};
+
+	for my $file (@{$json->{"files"}}) {
+		my $filename = $file->{"file"};
+
+		$data->{$filename} = $file;
+	}
+}
+
+
 #
 # intermediate_text_to_info(fd, data, srcdata)
 #
@@ -2173,6 +2219,104 @@ sub intermediate_text_to_info($$$)
 }
 
 
+#
+# intermediate_json_to_info(fd, data, srcdata)
+#
+# Write DATA in info format to file descriptor FD.
+#
+# data:      filename -> file_data:
+# file_data: GCOV JSON data for file
+#
+# 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_json_to_info($$$)
+{
+	my ($fd, $data, $srcdata) = @_;
+	my $branch_num = 0;
+
+	return if (!%{$data});
+
+	print($fd "TN:$test_name\n");
+	for my $filename (keys(%{$data})) {
+		my ($excl, $brexcl, $checksums);
+		my $file_data = $data->{$filename};
+
+		if (defined($srcdata->{$filename})) {
+			($excl, $brexcl, $checksums) = @{$srcdata->{$filename}};
+		}
+
+		print($fd "SF:$filename\n");
+
+		# Function data
+		if ($func_coverage) {
+			for my $d (@{$file_data->{"functions"}}) {
+				my $line = $d->{"start_line"};
+				my $count = $d->{"execution_count"};
+				my $name = $d->{"name"};
+
+				next if (!defined($line) || !defined($count) ||
+					 !defined($name) || $excl->{$line});
+
+				print($fd "FN:$line,$name\n");
+				print($fd "FNDA:$count,$name\n");
+			}
+		}
+
+		# Line data
+		for my $d (@{$file_data->{"lines"}}) {
+			my $line = $d->{"line_number"};
+			my $count = $d->{"count"};
+			my $c;
+			my $branches = $d->{"branches"};
+			my $unexec = $d->{"unexecuted_block"};
+
+			next if (!defined($line) || !defined($count) ||
+				 $excl->{$line});
+
+			if (defined($unexec) && $unexec && $count == 0) {
+				$unexec = 1;
+			} else {
+				$unexec = 0;
+			}
+
+			if ($checksum && exists($checksums->{$line})) {
+				$c = ",".$checksums->{$line};
+			} else {
+				$c = "";
+			}
+			print($fd "DA:$line,$count$c\n");
+
+			$branch_num = 0;
+			# Branch data
+			if ($br_coverage && !$brexcl->{$line}) {
+				for my $b (@$branches) {
+					my $brcount = $b->{"count"};
+
+					if (!defined($brcount) || $unexec) {
+						$brcount = "-";
+					}
+					print($fd "BRDA:$line,0,$branch_num,".
+					      "$brcount\n");
+
+					$branch_num++;
+				}
+			}
+
+		}
+
+		print($fd "end_of_record\n");
+	}
+}
+
+
 sub get_output_fd($$)
 {
 	my ($outfile, $file) = @_;
@@ -2243,6 +2387,8 @@ sub process_intermediate($$$)
 	my $srcdata;
 	my $is_graph = 0;
 	my ($out, $err, $rc);
+	my $json_basedir;
+	my $json_format;
 
 	info("Processing %s\n", abs2rel($file, $dir));
 
@@ -2296,6 +2442,12 @@ sub process_intermediate($$$)
 		unlink($gcov_filename);
 	}
 
+	for my $gcov_filename (glob("*.gcov.json.gz")) {
+		read_intermediate_json($gcov_filename, \%data, \$json_basedir);
+		unlink($gcov_filename);
+		$json_format = 1;
+	}
+
 	if (!%data) {
 		warn("WARNING: GCOV did not produce any data for $file\n");
 		return;
@@ -2304,6 +2456,8 @@ sub process_intermediate($$$)
 	# Determine base directory
 	if (defined($base_directory)) {
 		$base = $base_directory;
+	} elsif (defined($json_basedir)) {
+		$base = $json_basedir;
 	} else {
 		$base = $fdir;
 
@@ -2331,7 +2485,11 @@ sub process_intermediate($$$)
 
 	# Generate output
 	$fd = get_output_fd($output_filename, $file);
-	intermediate_text_to_info($fd, \%data, $srcdata);
+	if ($json_format) {
+		intermediate_json_to_info($fd, \%data, $srcdata);
+	} else {
+		intermediate_text_to_info($fd, \%data, $srcdata);
+	}
 	close($fd);
 
 	chdir($cwd);
-- 
2.17.1

