1From e13b2b6f8443da660cafa0679c3b16240843ce9f Mon Sep 17 00:00:00 2001
2From: Peter Oberparleiter <oberpar@linux.ibm.com>
3Date: Fri, 24 May 2019 17:16:56 +0200
4Subject: [PATCH 2/2] geninfo: Add intermediate JSON format support
5
6This change adds support for parsing the output of gcov's intermediate
7JSON file format as implemented by GCC version 9.
8
9Note: The way that the intermediate file format support is implemented
10in geninfo removes the need to parse .gcno files directly. Since geninfo
11does not include support for parsing GCC 9 .gcno files, using the
12intermediate format is the only option for geninfo to collect coverage
13data generated by GCC version 9.
14
15Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com>
16---
17 bin/geninfo | 162 +++++++++++++++++++++++++++++++++++++++++++++++++++-
18 1 file changed, 160 insertions(+), 2 deletions(-)
19
20Upstream-Status: Backport
21Download URL: https://github.com/linux-test-project/lcov/commit/75fbae1cfc5027f818a0bb865bf6f96fab3202da
22
23diff --git a/bin/geninfo b/bin/geninfo
24index 0276666..cceb782 100755
25--- a/bin/geninfo
26+++ b/bin/geninfo
27@@ -59,6 +59,9 @@ use File::Copy qw(copy);
28 use Getopt::Long;
29 use Digest::MD5 qw(md5_base64);
30 use Cwd qw/abs_path/;
31+use PerlIO::gzip;
32+use JSON qw(decode_json);
33+
34 if( $^O eq "msys" )
35 {
36 	require File::Spec::Win32;
37@@ -474,7 +477,8 @@ if ($rc_intermediate eq "0") {
38 	$intermediate = 1;
39 } elsif (lc($rc_intermediate) eq "auto") {
40 	# Use intermediate format if supported by gcov
41-	$intermediate = $gcov_caps->{'intermediate-format'} ? 1 : 0;
42+	$intermediate = ($gcov_caps->{'intermediate-format'} ||
43+			 $gcov_caps->{'json-format'}) ? 1 : 0;
44 } else {
45 	die("ERROR: invalid value for geninfo_intermediate: ".
46 	    "'$rc_intermediate'\n");
47@@ -2084,6 +2088,48 @@ sub read_intermediate_text($$)
48 }
49
50
51+#
52+# read_intermediate_json(gcov_filename, data, basedir_ref)
53+#
54+# Read gcov intermediate JSON format in GCOV_FILENAME and add the resulting
55+# data to DATA in the following format:
56+#
57+# data:      source_filename -> file_data
58+# file_data: GCOV JSON data for file
59+#
60+# Also store the value for current_working_directory to BASEDIR_REF.
61+#
62+
63+sub read_intermediate_json($$$)
64+{
65+	my ($gcov_filename, $data, $basedir_ref) = @_;
66+	my $fd;
67+	my $text;
68+	my $json;
69+
70+	open($fd, "<:gzip", $gcov_filename) or
71+		die("ERROR: Could not read $gcov_filename: $!\n");
72+	local $/;
73+	$text = <$fd>;
74+	close($fd);
75+
76+	$json = decode_json($text);
77+	if (!defined($json) || !exists($json->{"files"}) ||
78+	    ref($json->{"files"} ne "ARRAY")) {
79+		die("ERROR: Unrecognized JSON output format in ".
80+		    "$gcov_filename\n");
81+	}
82+
83+	$$basedir_ref = $json->{"current_working_directory"};
84+
85+	for my $file (@{$json->{"files"}}) {
86+		my $filename = $file->{"file"};
87+
88+		$data->{$filename} = $file;
89+	}
90+}
91+
92+
93 #
94 # intermediate_text_to_info(fd, data, srcdata)
95 #
96@@ -2173,6 +2219,104 @@ sub intermediate_text_to_info($$$)
97 }
98
99
100+#
101+# intermediate_json_to_info(fd, data, srcdata)
102+#
103+# Write DATA in info format to file descriptor FD.
104+#
105+# data:      filename -> file_data:
106+# file_data: GCOV JSON data for file
107+#
108+# srcdata:   filename -> [ excl, brexcl, checksums ]
109+# excl:      lineno -> 1 for all lines for which to exclude all data
110+# brexcl:    lineno -> 1 for all lines for which to exclude branch data
111+# checksums: lineno -> source code checksum
112+#
113+# Note: To simplify processing, gcov data is not combined here, that is counts
114+#       that appear multiple times for the same lines/branches are not added.
115+#       This is done by lcov/genhtml when reading the data files.
116+#
117+
118+sub intermediate_json_to_info($$$)
119+{
120+	my ($fd, $data, $srcdata) = @_;
121+	my $branch_num = 0;
122+
123+	return if (!%{$data});
124+
125+	print($fd "TN:$test_name\n");
126+	for my $filename (keys(%{$data})) {
127+		my ($excl, $brexcl, $checksums);
128+		my $file_data = $data->{$filename};
129+
130+		if (defined($srcdata->{$filename})) {
131+			($excl, $brexcl, $checksums) = @{$srcdata->{$filename}};
132+		}
133+
134+		print($fd "SF:$filename\n");
135+
136+		# Function data
137+		if ($func_coverage) {
138+			for my $d (@{$file_data->{"functions"}}) {
139+				my $line = $d->{"start_line"};
140+				my $count = $d->{"execution_count"};
141+				my $name = $d->{"name"};
142+
143+				next if (!defined($line) || !defined($count) ||
144+					 !defined($name) || $excl->{$line});
145+
146+				print($fd "FN:$line,$name\n");
147+				print($fd "FNDA:$count,$name\n");
148+			}
149+		}
150+
151+		# Line data
152+		for my $d (@{$file_data->{"lines"}}) {
153+			my $line = $d->{"line_number"};
154+			my $count = $d->{"count"};
155+			my $c;
156+			my $branches = $d->{"branches"};
157+			my $unexec = $d->{"unexecuted_block"};
158+
159+			next if (!defined($line) || !defined($count) ||
160+				 $excl->{$line});
161+
162+			if (defined($unexec) && $unexec && $count == 0) {
163+				$unexec = 1;
164+			} else {
165+				$unexec = 0;
166+			}
167+
168+			if ($checksum && exists($checksums->{$line})) {
169+				$c = ",".$checksums->{$line};
170+			} else {
171+				$c = "";
172+			}
173+			print($fd "DA:$line,$count$c\n");
174+
175+			$branch_num = 0;
176+			# Branch data
177+			if ($br_coverage && !$brexcl->{$line}) {
178+				for my $b (@$branches) {
179+					my $brcount = $b->{"count"};
180+
181+					if (!defined($brcount) || $unexec) {
182+						$brcount = "-";
183+					}
184+					print($fd "BRDA:$line,0,$branch_num,".
185+					      "$brcount\n");
186+
187+					$branch_num++;
188+				}
189+			}
190+
191+		}
192+
193+		print($fd "end_of_record\n");
194+	}
195+}
196+
197+
198 sub get_output_fd($$)
199 {
200 	my ($outfile, $file) = @_;
201@@ -2243,6 +2387,8 @@ sub process_intermediate($$$)
202 	my $srcdata;
203 	my $is_graph = 0;
204 	my ($out, $err, $rc);
205+	my $json_basedir;
206+	my $json_format;
207
208 	info("Processing %s\n", abs2rel($file, $dir));
209
210@@ -2296,6 +2442,12 @@ sub process_intermediate($$$)
211 		unlink($gcov_filename);
212 	}
213
214+	for my $gcov_filename (glob("*.gcov.json.gz")) {
215+		read_intermediate_json($gcov_filename, \%data, \$json_basedir);
216+		unlink($gcov_filename);
217+		$json_format = 1;
218+	}
219+
220 	if (!%data) {
221 		warn("WARNING: GCOV did not produce any data for $file\n");
222 		return;
223@@ -2304,6 +2456,8 @@ sub process_intermediate($$$)
224 	# Determine base directory
225 	if (defined($base_directory)) {
226 		$base = $base_directory;
227+	} elsif (defined($json_basedir)) {
228+		$base = $json_basedir;
229 	} else {
230 		$base = $fdir;
231
232@@ -2331,7 +2485,11 @@ sub process_intermediate($$$)
233
234 	# Generate output
235 	$fd = get_output_fd($output_filename, $file);
236-	intermediate_text_to_info($fd, \%data, $srcdata);
237+	if ($json_format) {
238+		intermediate_json_to_info($fd, \%data, $srcdata);
239+	} else {
240+		intermediate_text_to_info($fd, \%data, $srcdata);
241+	}
242 	close($fd);
243
244 	chdir($cwd);
245--
2462.17.1
247
248