diff --git a/bin/geninfo b/bin/geninfo index 5453356..4e7a293 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);