summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTobias Markmann <tm@ayena.de>2016-04-18 16:46:39 (GMT)
committerKevin Smith <kevin.smith@isode.com>2016-04-25 08:24:37 (GMT)
commit511c43aec78279ecaa6008bd7f4f08159d3a5c00 (patch)
treec5cefba4e25917eb4eb51e67aec0a3b96b5318cc /3rdParty
parent45cca954063503d0a30410c626e9b2c29acdf7e9 (diff)
downloadswift-511c43aec78279ecaa6008bd7f4f08159d3a5c00.zip
swift-511c43aec78279ecaa6008bd7f4f08159d3a5c00.tar.bz2
Update 3rdParty lcov to version 1.12
The old version caused errors when running BuildTools/Coverage/GenerateCoverageResults.sh. Test-Information: Successfully ran BuildTools/Coverage/GenerateCoverageResults.sh on OS X 10.11.4. Change-Id: If6f53a85e8051388c6d5b17d980f0c3fa8e65b6e
Diffstat (limited to '3rdParty')
-rwxr-xr-x3rdParty/LCov/gendesc11
-rwxr-xr-x3rdParty/LCov/genhtml672
-rwxr-xr-x3rdParty/LCov/geninfo1200
-rwxr-xr-x3rdParty/LCov/genpng20
-rwxr-xr-x3rdParty/LCov/lcov489
5 files changed, 1901 insertions, 491 deletions
diff --git a/3rdParty/LCov/gendesc b/3rdParty/LCov/gendesc
index 522ef69..7287c83 100755
--- a/3rdParty/LCov/gendesc
+++ b/3rdParty/LCov/gendesc
@@ -38,10 +38,12 @@
use strict;
use File::Basename;
use Getopt::Long;
+use Cwd qw/abs_path/;
# Constants
-our $lcov_version = 'LCOV version 1.9';
+our $tool_dir = abs_path(dirname($0));
+our $lcov_version = "LCOV version 1.12";
our $lcov_url = "http://ltp.sourceforge.net/coverage/lcov.php";
our $tool_name = basename($0);
@@ -67,9 +69,6 @@ our $input_filename;
$SIG{__WARN__} = \&warn_handler;
$SIG{__DIE__} = \&die_handler;
-# Prettify version string
-$lcov_version =~ s/\$\s*Revision\s*:?\s*(\S+)\s*\$/$1/;
-
# Parse command line options
if (!GetOptions("output-filename=s" => \$output_filename,
"version" =>\$version,
@@ -153,13 +152,13 @@ sub gen_desc()
local *OUTPUT_HANDLE;
my $empty_line = "ignore";
- open(INPUT_HANDLE, $input_filename)
+ open(INPUT_HANDLE, "<", $input_filename)
or die("ERROR: cannot open $input_filename!\n");
# Open output file for writing
if ($output_filename)
{
- open(OUTPUT_HANDLE, ">$output_filename")
+ open(OUTPUT_HANDLE, ">", $output_filename)
or die("ERROR: cannot create $output_filename!\n");
}
else
diff --git a/3rdParty/LCov/genhtml b/3rdParty/LCov/genhtml
index d74063a..cf1b7f5 100755
--- a/3rdParty/LCov/genhtml
+++ b/3rdParty/LCov/genhtml
@@ -1,6 +1,6 @@
#!/usr/bin/perl -w
#
-# Copyright (c) International Business Machines Corp., 2002,2010
+# Copyright (c) International Business Machines Corp., 2002,2012
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -65,17 +65,23 @@
#
use strict;
-use File::Basename;
+use File::Basename;
+use File::Temp qw(tempfile);
use Getopt::Long;
use Digest::MD5 qw(md5_base64);
+use Cwd qw/abs_path/;
# Global constants
our $title = "LCOV - code coverage report";
-our $lcov_version = 'LCOV version 1.9';
+our $tool_dir = abs_path(dirname($0));
+our $lcov_version = "LCOV version 1.12";
our $lcov_url = "http://ltp.sourceforge.net/coverage/lcov.php";
our $tool_name = basename($0);
+# Specify coverage rate default precision
+our $default_precision = 1;
+
# Specify coverage rate limits (in %) for classifying file entries
# HI: $hi_limit <= rate <= 100 graph color: green
# MED: $med_limit <= rate < $hi_limit graph color: orange
@@ -145,6 +151,7 @@ our $BR_BRANCH = 1;
our $BR_TAKEN = 2;
our $BR_VEC_ENTRIES = 3;
our $BR_VEC_WIDTH = 32;
+our $BR_VEC_MAX = vec(pack('b*', 1 x $BR_VEC_WIDTH), 0, $BR_VEC_WIDTH);
# Additional offsets used when converting branch coverage data to HTML
our $BR_LEN = 3;
@@ -155,6 +162,12 @@ our $BR_CLOSE = 5;
our $BR_SUB = 0;
our $BR_ADD = 1;
+# Error classes which users may specify to ignore during processing
+our $ERROR_SOURCE = 0;
+our %ERROR_ID = (
+ "source" => $ERROR_SOURCE,
+);
+
# Data related prototypes
sub print_usage(*);
sub gen_html();
@@ -165,7 +178,7 @@ sub info(@);
sub read_info_file($);
sub get_info_entry($);
sub set_info_entry($$$$$$$$$;$$$$$$);
-sub get_prefix(@);
+sub get_prefix($@);
sub shorten_prefix($);
sub get_dir_list(@);
sub get_relative_base_path($);
@@ -181,7 +194,7 @@ sub get_affecting_tests($$$);
sub combine_info_files($$);
sub merge_checksums($$$);
sub combine_info_entries($$$);
-sub apply_prefix($$);
+sub apply_prefix($@);
sub system_no_output($@);
sub read_config($);
sub apply_config($);
@@ -198,6 +211,9 @@ sub combine_brcount($$$);
sub get_br_found_and_hit($);
sub warn_handler($);
sub die_handler($);
+sub parse_ignore_errors(@);
+sub parse_dir_prefix(@);
+sub rate($$;$$$);
# HTML related prototypes
@@ -244,7 +260,8 @@ sub gen_png($$$@);
# Global variables & initialization
our %info_data; # Hash containing all data from .info file
-our $dir_prefix; # Prefix to remove from all sub directories
+our @opt_dir_prefix; # Array of prefixes to remove from all sub directories
+our @dir_prefix;
our %test_description; # Hash containing test descriptions if available
our $date = get_date_string();
@@ -259,9 +276,9 @@ our $help; # Help option flag
our $version; # Version option flag
our $show_details; # If set, generate detailed directory view
our $no_prefix; # If set, do not remove filename prefix
-our $func_coverage = 1; # If set, generate function coverage statistics
+our $func_coverage; # If set, generate function coverage statistics
our $no_func_coverage; # Disable func_coverage
-our $br_coverage = 1; # If set, generate branch coverage statistics
+our $br_coverage; # If set, generate branch coverage statistics
our $no_br_coverage; # Disable br_coverage
our $sort = 1; # If set, provide directory listings with sorted entries
our $no_sort; # Disable sort
@@ -279,15 +296,22 @@ our $html_epilog; # Actual HTML epilog
our $html_ext = "html"; # Extension for generated HTML files
our $html_gzip = 0; # Compress with gzip
our $demangle_cpp = 0; # Demangle C++ function names
+our @opt_ignore_errors; # Ignore certain error classes during processing
+our @ignore;
+our $opt_config_file; # User-specified configuration file location
+our %opt_rc;
+our $charset = "UTF-8"; # Default charset for HTML pages
our @fileview_sortlist;
our @fileview_sortname = ("", "-sort-l", "-sort-f", "-sort-b");
our @funcview_sortlist;
our @rate_name = ("Lo", "Med", "Hi");
our @rate_png = ("ruby.png", "amber.png", "emerald.png");
+our $lcov_func_coverage = 1;
+our $lcov_branch_coverage = 0;
+our $rc_desc_html = 0; # lcovrc: genhtml_desc_html
our $cwd = `pwd`; # Current working directory
chomp($cwd);
-our $tool_dir = dirname($0); # Directory where genhtml tool is installed
#
@@ -297,17 +321,29 @@ our $tool_dir = dirname($0); # Directory where genhtml tool is installed
$SIG{__WARN__} = \&warn_handler;
$SIG{__DIE__} = \&die_handler;
-# Prettify version string
-$lcov_version =~ s/\$\s*Revision\s*:?\s*(\S+)\s*\$/$1/;
+# Check command line for a configuration file name
+Getopt::Long::Configure("pass_through", "no_auto_abbrev");
+GetOptions("config-file=s" => \$opt_config_file,
+ "rc=s%" => \%opt_rc);
+Getopt::Long::Configure("default");
-# Add current working directory if $tool_dir is not already an absolute path
-if (! ($tool_dir =~ /^\/(.*)$/))
{
- $tool_dir = "$cwd/$tool_dir";
+ # Remove spaces around rc options
+ my %new_opt_rc;
+
+ while (my ($key, $value) = each(%opt_rc)) {
+ $key =~ s/^\s+|\s+$//g;
+ $value =~ s/^\s+|\s+$//g;
+
+ $new_opt_rc{$key} = $value;
+ }
+ %opt_rc = %new_opt_rc;
}
# Read configuration file if available
-if (defined($ENV{"HOME"}) && (-r $ENV{"HOME"}."/.lcovrc"))
+if (defined($opt_config_file)) {
+ $config = read_config($opt_config_file);
+} elsif (defined($ENV{"HOME"}) && (-r $ENV{"HOME"}."/.lcovrc"))
{
$config = read_config($ENV{"HOME"}."/.lcovrc");
}
@@ -316,9 +352,9 @@ elsif (-r "/etc/lcovrc")
$config = read_config("/etc/lcovrc");
}
-if ($config)
+if ($config || %opt_rc)
{
- # Copy configuration file values to variables
+ # Copy configuration file and --rc values to variables
apply_config({
"genhtml_css_file" => \$css_filename,
"genhtml_hi_limit" => \$hi_limit,
@@ -337,6 +373,7 @@ if ($config)
"genhtml_html_epilog" => \$html_epilog_file,
"genhtml_html_extension" => \$html_ext,
"genhtml_html_gzip" => \$html_gzip,
+ "genhtml_precision" => \$default_precision,
"genhtml_function_hi_limit" => \$fn_hi_limit,
"genhtml_function_med_limit" => \$fn_med_limit,
"genhtml_function_coverage" => \$func_coverage,
@@ -345,14 +382,20 @@ if ($config)
"genhtml_branch_coverage" => \$br_coverage,
"genhtml_branch_field_width" => \$br_field_width,
"genhtml_sort" => \$sort,
+ "genhtml_charset" => \$charset,
+ "genhtml_desc_html" => \$rc_desc_html,
+ "lcov_function_coverage" => \$lcov_func_coverage,
+ "lcov_branch_coverage" => \$lcov_branch_coverage,
});
}
-# Copy limit values if not specified
+# Copy related values if not specified
$fn_hi_limit = $hi_limit if (!defined($fn_hi_limit));
$fn_med_limit = $med_limit if (!defined($fn_med_limit));
$br_hi_limit = $hi_limit if (!defined($br_hi_limit));
$br_med_limit = $med_limit if (!defined($br_med_limit));
+$func_coverage = $lcov_func_coverage if (!defined($func_coverage));
+$br_coverage = $lcov_branch_coverage if (!defined($br_coverage));
# Parse command line options
if (!GetOptions("output-directory|o=s" => \$output_directory,
@@ -361,7 +404,7 @@ if (!GetOptions("output-directory|o=s" => \$output_directory,
"keep-descriptions|k" => \$keep_descriptions,
"css-file|c=s" => \$css_filename,
"baseline-file|b=s" => \$base_filename,
- "prefix|p=s" => \$dir_prefix,
+ "prefix|p=s" => \@opt_dir_prefix,
"num-spaces=i" => \$tab_size,
"no-prefix" => \$no_prefix,
"no-sourceview" => \$no_sourceview,
@@ -383,6 +426,10 @@ if (!GetOptions("output-directory|o=s" => \$output_directory,
"sort" => \$sort,
"no-sort" => \$no_sort,
"demangle-cpp" => \$demangle_cpp,
+ "ignore-errors=s" => \@opt_ignore_errors,
+ "config-file=s" => \$opt_config_file,
+ "rc=s%" => \%opt_rc,
+ "precision=i" => \$default_precision,
))
{
print(STDERR "Use $tool_name --help to get usage information\n");
@@ -418,6 +465,12 @@ if ($version)
exit(0);
}
+# Determine which errors the user wants us to ignore
+parse_ignore_errors(@opt_ignore_errors);
+
+# Split the list of prefixes if needed
+parse_dir_prefix(@opt_dir_prefix);
+
# Check for info filename
if (!@info_filenames)
{
@@ -471,11 +524,11 @@ if ($no_sourceview && defined($frames))
}
# Issue a warning if --no-prefix is enabled together with --prefix
-if ($no_prefix && defined($dir_prefix))
+if ($no_prefix && @dir_prefix)
{
warn("WARNING: option --prefix disabled because --no-prefix was ".
"specified!\n");
- $dir_prefix = undef;
+ @dir_prefix = undef;
}
@fileview_sortlist = ($SORT_FILE);
@@ -503,6 +556,13 @@ if ($demangle_cpp)
}
}
+# Make sure precision is within valid range
+if ($default_precision < 1 || $default_precision > 4)
+{
+ die("ERROR: specified precision is out of range (1 to 4)\n");
+}
+
+
# Make sure output_directory exists, create it if necessary
if ($output_directory)
{
@@ -541,6 +601,9 @@ Misc:
-h, --help Print this help, then exit
-v, --version Print version number, then exit
-q, --quiet Do not print progress messages
+ --config-file FILENAME Specify configuration file location
+ --rc SETTING=VALUE Override configuration file setting
+ --ignore-errors ERRORS Continue after ERRORS (source)
Operation:
-o, --output-directory OUTDIR Write HTML output to OUTDIR
@@ -567,6 +630,7 @@ HTML output:
--html-gzip Use gzip to compress HTML
--(no-)sort Enable (disable) sorted coverage views
--demangle-cpp Demangle C++ function names
+ --precision NUM Set precision of coverage rate
For more information see: $lcov_url
END_OF_USAGE
@@ -607,8 +671,7 @@ sub get_overall_line($$$$)
return "no data found" if (!defined($found) || $found == 0);
$name = ($found == 1) ? $name_sn : $name_pl;
- return sprintf("%.1f%% (%d of %d %s)", $hit * 100 / $found, $hit,
- $found, $name);
+ return rate($hit, $found, "% ($hit of $found $name)");
}
@@ -636,6 +699,116 @@ sub print_overall_rate($$$$$$$$$)
if ($br_do);
}
+sub get_fn_list($)
+{
+ my ($info) = @_;
+ my %fns;
+ my @result;
+
+ foreach my $filename (keys(%{$info})) {
+ my $data = $info->{$filename};
+ my $funcdata = $data->{"func"};
+ my $sumfnccount = $data->{"sumfnc"};
+
+ if (defined($funcdata)) {
+ foreach my $func_name (keys(%{$funcdata})) {
+ $fns{$func_name} = 1;
+ }
+ }
+
+ if (defined($sumfnccount)) {
+ foreach my $func_name (keys(%{$sumfnccount})) {
+ $fns{$func_name} = 1;
+ }
+ }
+ }
+
+ @result = keys(%fns);
+
+ return \@result;
+}
+
+#
+# rename_functions(info, conv)
+#
+# Rename all function names in INFO according to CONV: OLD_NAME -> NEW_NAME.
+# In case two functions demangle to the same name, assume that they are
+# different object code implementations for the same source function.
+#
+
+sub rename_functions($$)
+{
+ my ($info, $conv) = @_;
+
+ foreach my $filename (keys(%{$info})) {
+ my $data = $info->{$filename};
+ my $funcdata;
+ my $testfncdata;
+ my $sumfnccount;
+ my %newfuncdata;
+ my %newsumfnccount;
+ my $f_found;
+ my $f_hit;
+
+ # funcdata: function name -> line number
+ $funcdata = $data->{"func"};
+ foreach my $fn (keys(%{$funcdata})) {
+ my $cn = $conv->{$fn};
+
+ # Abort if two functions on different lines map to the
+ # same demangled name.
+ if (defined($newfuncdata{$cn}) &&
+ $newfuncdata{$cn} != $funcdata->{$fn}) {
+ die("ERROR: Demangled function name $cn ".
+ "maps to different lines (".
+ $newfuncdata{$cn}." vs ".
+ $funcdata->{$fn}.") in $filename\n");
+ }
+ $newfuncdata{$cn} = $funcdata->{$fn};
+ }
+ $data->{"func"} = \%newfuncdata;
+
+ # testfncdata: test name -> testfnccount
+ # testfnccount: function name -> execution count
+ $testfncdata = $data->{"testfnc"};
+ foreach my $tn (keys(%{$testfncdata})) {
+ my $testfnccount = $testfncdata->{$tn};
+ my %newtestfnccount;
+
+ foreach my $fn (keys(%{$testfnccount})) {
+ my $cn = $conv->{$fn};
+
+ # Add counts for different functions that map
+ # to the same name.
+ $newtestfnccount{$cn} +=
+ $testfnccount->{$fn};
+ }
+ $testfncdata->{$tn} = \%newtestfnccount;
+ }
+
+ # sumfnccount: function name -> execution count
+ $sumfnccount = $data->{"sumfnc"};
+ foreach my $fn (keys(%{$sumfnccount})) {
+ my $cn = $conv->{$fn};
+
+ # Add counts for different functions that map
+ # to the same name.
+ $newsumfnccount{$cn} += $sumfnccount->{$fn};
+ }
+ $data->{"sumfnc"} = \%newsumfnccount;
+
+ # Update function found and hit counts since they may have
+ # changed
+ $f_found = 0;
+ $f_hit = 0;
+ foreach my $fn (keys(%newsumfnccount)) {
+ $f_found++;
+ $f_hit++ if ($newsumfnccount{$fn} > 0);
+ }
+ $data->{"f_found"} = $f_found;
+ $data->{"f_hit"} = $f_hit;
+ }
+}
#
# gen_html()
@@ -701,14 +874,16 @@ sub gen_html()
# User requested that we leave filenames alone
info("User asked not to remove filename prefix\n");
}
- elsif (!defined($dir_prefix))
+ elsif (! @dir_prefix)
{
# Get prefix common to most directories in list
- $dir_prefix = get_prefix(@dir_list);
+ my $prefix = get_prefix(1, keys(%info_data));
- if ($dir_prefix)
+ if ($prefix)
{
- info("Found common filename prefix \"$dir_prefix\"\n");
+ info("Found common filename prefix \"$prefix\"\n");
+ $dir_prefix[0] = $prefix;
+
}
else
{
@@ -718,10 +893,17 @@ sub gen_html()
}
else
{
- info("Using user-specified filename prefix \"".
- "$dir_prefix\"\n");
+ my $msg = "Using user-specified filename prefix ";
+ for my $i (0 .. $#dir_prefix)
+ {
+ $dir_prefix[$i] =~ s/\/+$//;
+ $msg .= ", " unless 0 == $i;
+ $msg .= "\"" . $dir_prefix[$i] . "\"";
+ }
+ info($msg . "\n");
}
+
# Read in test description file if specified
if ($desc_filename)
{
@@ -763,11 +945,14 @@ sub gen_html()
$br_found, $br_hit)
= process_dir($dir_name);
+ # Handle files in root directory gracefully
+ $dir_name = "root" if ($dir_name eq "");
+
# Remove prefix if applicable
- if (!$no_prefix && $dir_prefix)
+ if (!$no_prefix && @dir_prefix)
{
- # Match directory names beginning with $dir_prefix
- $dir_name = apply_prefix($dir_name, $dir_prefix);
+ # Match directory names beginning with one of @dir_prefix
+ $dir_name = apply_prefix($dir_name,@dir_prefix);
}
# Generate name for directory overview HTML page
@@ -832,13 +1017,13 @@ sub html_create($$)
if ($html_gzip)
{
- open($handle, "|gzip -c >$filename")
+ open($handle, "|-", "gzip -c >'$filename'")
or die("ERROR: cannot open $filename for writing ".
"(gzip)!\n");
}
else
{
- open($handle, ">$filename")
+ open($handle, ">", $filename)
or die("ERROR: cannot open $filename for writing!\n");
}
}
@@ -855,6 +1040,7 @@ sub write_dir_page($$$$$$$$$$$$$$$$$)
if (!defined($trunc_dir)) {
$trunc_dir = "";
}
+ $title .= " - " if ($trunc_dir ne "");
write_html_prolog(*HTML_HANDLE, $base_dir, "LCOV - $title$trunc_dir");
write_header(*HTML_HANDLE, $view_type, $trunc_dir, $rel_dir,
$overall_found, $overall_hit, $total_fn_found,
@@ -904,8 +1090,8 @@ sub process_dir($)
# Remove prefix if applicable
if (!$no_prefix)
{
- # Match directory name beginning with $dir_prefix
- $rel_dir = apply_prefix($rel_dir, $dir_prefix);
+ # Match directory name beginning with one of @dir_prefix
+ $rel_dir = apply_prefix($rel_dir,@dir_prefix);
}
$trunc_dir = $rel_dir;
@@ -916,6 +1102,10 @@ sub process_dir($)
$rel_dir = substr($rel_dir, 1);
}
+ # Handle files in root directory gracefully
+ $rel_dir = "root" if ($rel_dir eq "");
+ $trunc_dir = "root" if ($trunc_dir eq "");
+
$base_dir = get_relative_base_path($rel_dir);
create_sub_dir($rel_dir);
@@ -1083,7 +1273,7 @@ sub write_function_page($$$$$$$$$$$$$$$$$$)
sub process_file($$$)
{
- info("Processing file ".apply_prefix($_[2], $dir_prefix)."\n");
+ info("Processing file ".apply_prefix($_[2], @dir_prefix)."\n");
my $trunc_dir = $_[0];
my $rel_dir = $_[1];
@@ -1195,6 +1385,10 @@ sub process_file($$$)
# "func" -> \%funcdata
# "found" -> $lines_found (number of instrumented lines found in file)
# "hit" -> $lines_hit (number of executed lines in file)
+# "f_found" -> $fn_found (number of instrumented functions found in file)
+# "f_hit" -> $fn_hit (number of executed functions in file)
+# "b_found" -> $br_found (number of instrumented branches found in file)
+# "b_hit" -> $br_hit (number of executed branches in file)
# "check" -> \%checkdata
# "testfnc" -> \%testfncdata
# "sumfnc" -> \%sumfnccount
@@ -1251,6 +1445,7 @@ sub read_info_file($)
my $line_checksum; # Checksum of current line
my $br_found;
my $br_hit;
+ my $notified_about_relative_paths;
local *INFO_HANDLE; # Filehandle for .info file
info("Reading data file $tracefile\n");
@@ -1281,14 +1476,14 @@ sub read_info_file($)
"compressed file $_[0]!\n");
# Open compressed file
- open(INFO_HANDLE, "gunzip -c $_[0]|")
+ open(INFO_HANDLE, "-|", "gunzip -c '$_[0]'")
or die("ERROR: cannot start gunzip to decompress ".
"file $_[0]!\n");
}
else
{
# Open decompressed file
- open(INFO_HANDLE, $_[0])
+ open(INFO_HANDLE, "<", $_[0])
or die("ERROR: cannot read file $_[0]!\n");
}
@@ -1317,7 +1512,16 @@ sub read_info_file($)
{
# Filename information found
# Retrieve data for new entry
- $filename = $1;
+ $filename = File::Spec->rel2abs($1, Cwd::cwd());
+
+ if (!File::Spec->file_name_is_absolute($1) &&
+ !$notified_about_relative_paths)
+ {
+ info("Resolved relative source file ".
+ "path \"$1\" with CWD to ".
+ "\"$filename\".\n");
+ $notified_about_relative_paths = 1;
+ }
$data = $result{$filename};
($testdata, $sumcount, $funcdata, $checkdata,
@@ -1379,6 +1583,8 @@ sub read_info_file($)
/^FN:(\d+),([^,]+)/ && do
{
+ last if (!$func_coverage);
+
# Function data found, add to structure
$funcdata->{$2} = $1;
@@ -1397,6 +1603,7 @@ sub read_info_file($)
/^FNDA:(\d+),([^,]+)/ && do
{
+ last if (!$func_coverage);
# Function call count found, add to structure
# Add summary counts
$sumfnccount->{$2} += $1;
@@ -1414,6 +1621,7 @@ sub read_info_file($)
my ($line, $block, $branch, $taken) =
($1, $2, $3, $4);
+ last if (!$br_coverage);
$sumbrcount->{$line} =
br_ivec_push($sumbrcount->{$line},
$block, $branch, $taken);
@@ -1614,8 +1822,8 @@ sub set_info_entry($$$$$$$$$;$$$$$$)
sub add_counts($$)
{
- my %data1 = %{$_[0]}; # Hash 1
- my %data2 = %{$_[1]}; # Hash 2
+ my $data1_ref = $_[0]; # Hash 1
+ my $data2_ref = $_[1]; # Hash 2
my %result; # Resulting hash
my $line; # Current line iteration scalar
my $data1_count; # Count of line in hash1
@@ -1623,10 +1831,10 @@ sub add_counts($$)
my $found = 0; # Total number of lines found
my $hit = 0; # Number of lines with a count > 0
- foreach $line (keys(%data1))
+ foreach $line (keys(%$data1_ref))
{
- $data1_count = $data1{$line};
- $data2_count = $data2{$line};
+ $data1_count = $data1_ref->{$line};
+ $data2_count = $data2_ref->{$line};
# Add counts if present in both hashes
if (defined($data2_count)) { $data1_count += $data2_count; }
@@ -1638,14 +1846,14 @@ sub add_counts($$)
if ($data1_count > 0) { $hit++; }
}
- # Add lines unique to data2
- foreach $line (keys(%data2))
+ # Add lines unique to data2_ref
+ foreach $line (keys(%$data2_ref))
{
- # Skip lines already in data1
- if (defined($data1{$line})) { next; }
+ # Skip lines already in data1_ref
+ if (defined($data1_ref->{$line})) { next; }
- # Copy count from data2
- $result{$line} = $data2{$line};
+ # Copy count from data2_ref
+ $result{$line} = $data2_ref->{$line};
$found++;
if ($result{$line} > 0) { $hit++; }
@@ -2110,16 +2318,17 @@ sub combine_info_files($$)
#
-# get_prefix(filename_list)
+# get_prefix(min_dir, filename_list)
#
# Search FILENAME_LIST for a directory prefix which is common to as many
# list entries as possible, so that removing this prefix will minimize the
-# sum of the lengths of all resulting shortened filenames.
+# sum of the lengths of all resulting shortened filenames while observing
+# that no filename has less than MIN_DIR parent directories.
#
-sub get_prefix(@)
+sub get_prefix($@)
{
- my @filename_list = @_; # provided list of filenames
+ my ($min_dir, @filename_list) = @_;
my %prefix; # mapping: prefix -> sum of lengths
my $current; # Temporary iteration variable
@@ -2128,12 +2337,14 @@ sub get_prefix(@)
{
# Need explicit assignment to get a copy of $_ so that
# shortening the contained prefix does not affect the list
- $current = shorten_prefix($_);
+ $current = $_;
while ($current = shorten_prefix($current))
{
+ $current .= "/";
+
# Skip rest if the remaining prefix has already been
# added to hash
- if ($prefix{$current}) { last; }
+ if (exists($prefix{$current})) { last; }
# Initialize with 0
$prefix{$current}="0";
@@ -2141,6 +2352,20 @@ sub get_prefix(@)
}
+ # Remove all prefixes that would cause filenames to have less than
+ # the minimum number of parent directories
+ foreach my $filename (@filename_list) {
+ my $dir = dirname($filename);
+
+ for (my $i = 0; $i < $min_dir; $i++) {
+ delete($prefix{$dir."/"});
+ $dir = shorten_prefix($dir);
+ }
+ }
+
+ # Check if any prefix remains
+ return undef if (!%prefix);
+
# Calculate sum of lengths for all prefixes
foreach $current (keys(%prefix))
{
@@ -2169,6 +2394,8 @@ sub get_prefix(@)
}
}
+ $current =~ s/\/$//;
+
return($current);
}
@@ -2260,7 +2487,7 @@ sub read_testfile($)
my $changed_testname;
local *TEST_HANDLE;
- open(TEST_HANDLE, "<".$_[0])
+ open(TEST_HANDLE, "<", $_[0])
or die("ERROR: cannot open $_[0]!\n");
while (<TEST_HANDLE>)
@@ -2281,6 +2508,9 @@ sub read_testfile($)
# Match lines beginning with TD:<whitespace(s)>
if (/^TD:\s+(.*?)\s*$/)
{
+ if (!defined($test_name)) {
+ die("ERROR: Found test description without prior test name in $_[0]:$.\n");
+ }
# Check for empty line
if ($1)
{
@@ -2348,10 +2578,15 @@ sub get_date_string()
my $year;
my $month;
my $day;
+ my $hour;
+ my $min;
+ my $sec;
- ($year, $month, $day) = (localtime())[5, 4, 3];
+ ($year, $month, $day, $hour, $min, $sec) =
+ (localtime())[5, 4, 3, 2, 1, 0];
- return sprintf("%d-%02d-%02d", $year+1900, $month+1, $day);
+ return sprintf("%d-%02d-%02d %02d:%02d:%02d", $year+1900, $month+1,
+ $day, $hour, $min, $sec);
}
@@ -2408,8 +2643,10 @@ sub write_description_file($$$$$$$)
foreach $test_name (sort(keys(%description)))
{
- write_test_table_entry(*HTML_HANDLE, $test_name,
- escape_html($description{$test_name}));
+ my $desc = $description{$test_name};
+
+ $desc = escape_html($desc) if (!$rc_desc_html);
+ write_test_table_entry(*HTML_HANDLE, $test_name, $desc);
}
write_test_table_epilog(*HTML_HANDLE);
@@ -2531,7 +2768,7 @@ sub write_png_files()
0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82] if ($sort);
foreach (keys(%data))
{
- open(PNG_HANDLE, ">".$_)
+ open(PNG_HANDLE, ">", $_)
or die("ERROR: cannot create $_!\n");
binmode(PNG_HANDLE);
print(PNG_HANDLE map(chr,@{$data{$_}}));
@@ -2549,7 +2786,7 @@ sub write_htaccess_file()
local *HTACCESS_HANDLE;
my $htaccess_data;
- open(*HTACCESS_HANDLE, ">.htaccess")
+ open(*HTACCESS_HANDLE, ">", ".htaccess")
or die("ERROR: cannot open .htaccess for writing!\n");
$htaccess_data = (<<"END_OF_HTACCESS")
@@ -2582,7 +2819,7 @@ sub write_css_file()
return;
}
- open(CSS_HANDLE, ">gcov.css")
+ open(CSS_HANDLE, ">", "gcov.css")
or die ("ERROR: cannot open gcov.css for writing!\n");
@@ -3132,6 +3369,7 @@ END_OF_CSS
sub get_bar_graph_code($$$)
{
+ my ($base_dir, $found, $hit) = @_;
my $rate;
my $alt;
my $width;
@@ -3142,13 +3380,12 @@ sub get_bar_graph_code($$$)
# Check number of instrumented lines
if ($_[1] == 0) { return ""; }
- $rate = $_[2] * 100 / $_[1];
- $alt = sprintf("%.1f", $rate)."%";
- $width = sprintf("%.0f", $rate);
- $remainder = sprintf("%d", 100-$width);
+ $alt = rate($hit, $found, "%");
+ $width = rate($hit, $found, undef, 0);
+ $remainder = 100 - $width;
# Decide which .png file to use
- $png_name = $rate_png[classify_rate($_[1], $_[2], $med_limit,
+ $png_name = $rate_png[classify_rate($found, $hit, $med_limit,
$hi_limit)];
if ($width == 0)
@@ -3197,7 +3434,7 @@ sub classify_rate($$$$)
if ($found == 0) {
return 2;
}
- $rate = $hit * 100 / $found;
+ $rate = rate($hit, $found);
if ($rate < $med) {
return 0;
} elsif ($rate < $hi) {
@@ -3415,12 +3652,13 @@ sub write_file_table_entry(*$$$@)
my ($handle, $base_dir, $filename, $page_link, @entries) = @_;
my $file_code;
my $entry;
+ my $esc_filename = escape_html($filename);
# Add link to source if provided
if (defined($page_link) && $page_link ne "") {
- $file_code = "<a href=\"$page_link\">$filename</a>";
+ $file_code = "<a href=\"$page_link\">$esc_filename</a>";
} else {
- $file_code = $filename;
+ $file_code = $esc_filename;
}
# First column: filename
@@ -3450,7 +3688,7 @@ END_OF_HTML
$rate = "-";
$class = "Hi";
} else {
- $rate = sprintf("%.1f&nbsp;%%", $hit * 100 / $found);
+ $rate = rate($hit, $found, "&nbsp;%");
$class = $rate_name[classify_rate($found, $hit,
$med, $hi)];
}
@@ -3490,11 +3728,8 @@ END_OF_HTML
# Test data
foreach $entry (@entries) {
my ($found, $hit) = @{$entry};
- my $rate = "-";
+ my $rate = rate($hit, $found, "&nbsp;%");
- if ($found > 0) {
- $rate = sprintf("%.1f&nbsp;%%", $hit * 100 / $found);
- }
write_html($handle, <<END_OF_HTML);
<td class="testPer">$rate</td>
<td class="testNum">$hit&nbsp;/&nbsp;$found</td>
@@ -4010,7 +4245,7 @@ sub write_frameset(*$$$)
<html lang="en">
<head>
- <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+ <meta http-equiv="Content-Type" content="text/html; charset=$charset">
<title>$_[3]</title>
<link rel="stylesheet" type="text/css" href="$_[1]gcov.css">
</head>
@@ -4073,7 +4308,7 @@ sub write_overview(*$$$$)
<head>
<title>$_[3]</title>
- <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+ <meta http-equiv="Content-Type" content="text/html; charset=$charset">
<link rel="stylesheet" type="text/css" href="$_[1]gcov.css">
</head>
@@ -4120,17 +4355,6 @@ END_OF_HTML
}
-# format_rate(found, hit)
-#
-# Return formatted percent string for coverage rate.
-#
-
-sub format_rate($$)
-{
- return $_[0] == 0 ? "-" : sprintf("%.1f", $_[1] * 100 / $_[0])." %";
-}
-
-
sub max($$)
{
my ($a, $b) = @_;
@@ -4172,6 +4396,7 @@ sub write_header(*$$$$$$$$$$)
my @row_right;
my $num_rows;
my $i;
+ my $esc_trunc_name = escape_html($trunc_name);
$base_name = basename($rel_filename);
@@ -4187,12 +4412,14 @@ sub write_header(*$$$$$$$$$$)
# Directory overview
$base_dir = get_relative_base_path($rel_filename);
$view = "<a href=\"$base_dir"."index.$html_ext\">".
- "$overview_title</a> - $trunc_name";
+ "$overview_title</a> - $esc_trunc_name";
}
elsif ($type == $HDR_SOURCE || $type == $HDR_FUNC)
{
# File view
my $dir_name = dirname($rel_filename);
+ my $esc_base_name = escape_html($base_name);
+ my $esc_dir_name = escape_html($dir_name);
$base_dir = get_relative_base_path($dir_name);
if ($frames)
@@ -4202,21 +4429,25 @@ sub write_header(*$$$$$$$$$$)
$view = "<a href=\"$base_dir"."index.$html_ext\" ".
"target=\"_parent\">$overview_title</a> - ".
"<a href=\"index.$html_ext\" target=\"_parent\">".
- "$dir_name</a> - $base_name";
+ "$esc_dir_name</a> - $esc_base_name";
}
else
{
$view = "<a href=\"$base_dir"."index.$html_ext\">".
"$overview_title</a> - ".
"<a href=\"index.$html_ext\">".
- "$dir_name</a> - $base_name";
+ "$esc_dir_name</a> - $esc_base_name";
}
# Add function suffix
if ($func_coverage) {
$view .= "<span style=\"font-size: 80%;\">";
if ($type == $HDR_SOURCE) {
- $view .= " (source / <a href=\"$base_name.func.$html_ext\">functions</a>)";
+ if ($sort) {
+ $view .= " (source / <a href=\"$base_name.func-sort-c.$html_ext\">functions</a>)";
+ } else {
+ $view .= " (source / <a href=\"$base_name.func.$html_ext\">functions</a>)";
+ }
} elsif ($type == $HDR_FUNC) {
$view .= " (<a href=\"$base_name.gcov.$html_ext\">source</a> / functions)";
}
@@ -4303,7 +4534,7 @@ END_OF_HTML
# Line coverage
$style = $rate_name[classify_rate($lines_found, $lines_hit,
$med_limit, $hi_limit)];
- $rate = format_rate($lines_found, $lines_hit);
+ $rate = rate($lines_hit, $lines_found, " %");
push(@row_right, [[undef, "headerItem", "Lines:"],
[undef, "headerCovTableEntry", $lines_hit],
[undef, "headerCovTableEntry", $lines_found],
@@ -4313,7 +4544,7 @@ END_OF_HTML
if ($func_coverage) {
$style = $rate_name[classify_rate($fn_found, $fn_hit,
$fn_med_limit, $fn_hi_limit)];
- $rate = format_rate($fn_found, $fn_hit);
+ $rate = rate($fn_hit, $fn_found, " %");
push(@row_right, [[undef, "headerItem", "Functions:"],
[undef, "headerCovTableEntry", $fn_hit],
[undef, "headerCovTableEntry", $fn_found],
@@ -4324,7 +4555,7 @@ END_OF_HTML
if ($br_coverage) {
$style = $rate_name[classify_rate($br_found, $br_hit,
$br_med_limit, $br_hi_limit)];
- $rate = format_rate($br_found, $br_hit);
+ $rate = rate($br_hit, $br_found, " %");
push(@row_right, [[undef, "headerItem", "Branches:"],
[undef, "headerCovTableEntry", $br_hit],
[undef, "headerCovTableEntry", $br_found],
@@ -4795,6 +5026,7 @@ sub br_ivec_get($$)
# Retrieve data from vector
$block = vec($vec, $offset + $BR_BLOCK, $BR_VEC_WIDTH);
+ $block = -1 if ($block == $BR_VEC_MAX);
$branch = vec($vec, $offset + $BR_BRANCH, $BR_VEC_WIDTH);
$taken = vec($vec, $offset + $BR_TAKEN, $BR_VEC_WIDTH);
@@ -4820,10 +5052,12 @@ sub br_ivec_push($$$$)
my $i;
$vec = "" if (!defined($vec));
+ $block = $BR_VEC_MAX if $block < 0;
# Check if branch already exists in vector
for ($i = 0; $i < $num; $i++) {
my ($v_block, $v_branch, $v_taken) = br_ivec_get($vec, $i);
+ $v_block = $BR_VEC_MAX if $v_block < 0;
next if ($v_block != $block || $v_branch != $branch);
@@ -4958,19 +5192,43 @@ sub write_source($$$$$$$)
my $sumbrcount = $_[6];
my $datafunc = get_hash_reverse($funcdata);
my $add_anchor;
+ my @file;
if ($_[2])
{
%count_data = %{$_[2]};
}
- open(SOURCE_HANDLE, "<".$source_filename)
- or die("ERROR: cannot open $source_filename for reading!\n");
+ if (!open(SOURCE_HANDLE, "<", $source_filename)) {
+ my @lines;
+ my $last_line = 0;
+
+ if (!$ignore[$ERROR_SOURCE]) {
+ die("ERROR: cannot read $source_filename\n");
+ }
+
+ # Continue without source file
+ warn("WARNING: cannot read $source_filename!\n");
+
+ @lines = sort( { $a <=> $b } keys(%count_data));
+ if (@lines) {
+ $last_line = $lines[scalar(@lines) - 1];
+ }
+ return ( ":" ) if ($last_line < 1);
+
+ # Simulate gcov behavior
+ for ($line_number = 1; $line_number <= $last_line;
+ $line_number++) {
+ push(@file, "/* EOF */");
+ }
+ } else {
+ @file = <SOURCE_HANDLE>;
+ }
write_source_prolog(*HTML_HANDLE);
-
- for ($line_number = 1; <SOURCE_HANDLE> ; $line_number++)
- {
+ $line_number = 0;
+ foreach (@file) {
+ $line_number++;
chomp($_);
# Also remove CR from line-end
@@ -5055,8 +5313,51 @@ sub funcview_get_sorted($$$)
if ($type == 0) {
return sort(keys(%{$funcdata}));
}
- return sort({$sumfncdata->{$b} <=> $sumfncdata->{$a}}
- keys(%{$sumfncdata}));
+ return sort({
+ $sumfncdata->{$b} == $sumfncdata->{$a} ?
+ $a cmp $b : $sumfncdata->{$a} <=> $sumfncdata->{$b}
+ } keys(%{$sumfncdata}));
+}
+
+sub demangle_list($)
+{
+ my ($list) = @_;
+ my $tmpfile;
+ my $handle;
+ my %demangle;
+ my %versions;
+
+ # Write function names to file
+ ($handle, $tmpfile) = tempfile();
+ die("ERROR: could not create temporary file") if (!defined($tmpfile));
+ print($handle join("\n", @$list));
+ close($handle);
+
+ # Build translation hash from c++filt output
+ open($handle, "-|", "c++filt < $tmpfile") or
+ die("ERROR: could not run c++filt: $!\n");
+ foreach my $func (@$list) {
+ my $translated = <$handle>;
+ my $version;
+
+ last if (!defined($translated));
+ chomp($translated);
+
+ $version = ++$versions{$translated};
+ $translated .= ".$version" if ($version > 1);
+ $demangle{$func} = $translated;
+ }
+ close($handle);
+
+ if (scalar(keys(%demangle)) != scalar(@$list)) {
+ die("ERROR: c++filt output not as expected (".
+ scalar(keys(%demangle))." vs ".scalar(@$list).") lines\n");
+ }
+
+ unlink($tmpfile) or
+ warn("WARNING: could not remove temporary file $tmpfile: $!\n");
+
+ return \%demangle;
}
#
@@ -5086,6 +5387,7 @@ sub write_function_table(*$$$$$$$$$$)
my $func;
my $func_code;
my $count_code;
+ my $demangle;
# Get HTML code for headings
$func_code = funcview_get_func_code($name, $base, $type);
@@ -5100,7 +5402,12 @@ sub write_function_table(*$$$$$$$$$$)
</tr>
END_OF_HTML
;
-
+
+ # Get demangle translation hash
+ if ($demangle_cpp) {
+ $demangle = demangle_list([ sort(keys(%{$funcdata})) ]);
+ }
+
# Get a sorted table
foreach $func (funcview_get_sorted($funcdata, $sumfncdata, $type)) {
if (!defined($funcdata->{$func}))
@@ -5113,12 +5420,10 @@ END_OF_HTML
my $count = $sumfncdata->{$name};
my $countstyle;
- # Demangle C++ function names if requested
- if ($demangle_cpp) {
- $name = `c++filt "$name"`;
- chomp($name);
- }
- # Escape any remaining special characters
+ # Replace function name with demangled version if available
+ $name = $demangle->{$name} if (exists($demangle->{$name}));
+
+ # Escape special characters
$name = escape_html($name);
if ($startline < 1) {
$startline = 1;
@@ -5412,22 +5717,25 @@ sub remove_unused_descriptions()
#
-# apply_prefix(filename, prefix)
+# apply_prefix(filename, PREFIXES)
#
-# If FILENAME begins with PREFIX, remove PREFIX from FILENAME and return
-# resulting string, otherwise return FILENAME.
+# If FILENAME begins with PREFIX from PREFIXES, remove PREFIX from FILENAME
+# and return resulting string, otherwise return FILENAME.
#
-sub apply_prefix($$)
+sub apply_prefix($@)
{
- my $filename = $_[0];
- my $prefix = $_[1];
+ my $filename = shift;
+ my @dir_prefix = @_;
- if (defined($prefix) && ($prefix ne ""))
+ if (@dir_prefix)
{
- if ($filename =~ /^\Q$prefix\E\/(.*)$/)
+ foreach my $prefix (@dir_prefix)
{
- return substr($filename, length($prefix) + 1);
+ if ($prefix ne "" && $filename =~ /^\Q$prefix\E\/(.*)$/)
+ {
+ return substr($filename, length($prefix) + 1);
+ }
}
}
@@ -5455,12 +5763,12 @@ sub system_no_output($@)
local *OLD_STDOUT;
# Save old stdout and stderr handles
- ($mode & 1) && open(OLD_STDOUT, ">>&STDOUT");
- ($mode & 2) && open(OLD_STDERR, ">>&STDERR");
+ ($mode & 1) && open(OLD_STDOUT, ">>&", "STDOUT");
+ ($mode & 2) && open(OLD_STDERR, ">>&", "STDERR");
# Redirect to /dev/null
- ($mode & 1) && open(STDOUT, ">/dev/null");
- ($mode & 2) && open(STDERR, ">/dev/null");
+ ($mode & 1) && open(STDOUT, ">", "/dev/null");
+ ($mode & 2) && open(STDERR, ">", "/dev/null");
system(@_);
$result = $?;
@@ -5470,8 +5778,8 @@ sub system_no_output($@)
($mode & 2) && close(STDERR);
# Restore old handles
- ($mode & 1) && open(STDOUT, ">>&OLD_STDOUT");
- ($mode & 2) && open(STDERR, ">>&OLD_STDERR");
+ ($mode & 1) && open(STDOUT, ">>&", "OLD_STDOUT");
+ ($mode & 2) && open(STDERR, ">>&", "OLD_STDERR");
return $result;
}
@@ -5492,7 +5800,7 @@ sub read_config($)
my $value;
local *HANDLE;
- if (!open(HANDLE, "<$filename"))
+ if (!open(HANDLE, "<", $filename))
{
warn("WARNING: cannot read configuration file $filename\n");
return undef;
@@ -5531,8 +5839,8 @@ sub read_config($)
# key_string => var_ref
#
# where KEY_STRING is a keyword and VAR_REF is a reference to an associated
-# variable. If the global configuration hash CONFIG contains a value for
-# keyword KEY_STRING, VAR_REF will be assigned the value for that keyword.
+# variable. If the global configuration hashes CONFIG or OPT_RC contain a value
+# for keyword KEY_STRING, VAR_REF will be assigned the value for that keyword.
#
sub apply_config($)
@@ -5541,8 +5849,9 @@ sub apply_config($)
foreach (keys(%{$ref}))
{
- if (defined($config->{$_}))
- {
+ if (defined($opt_rc{$_})) {
+ ${$ref->{$_}} = $opt_rc{$_};
+ } elsif (defined($config->{$_})) {
${$ref->{$_}} = $config->{$_};
}
}
@@ -5565,7 +5874,7 @@ sub get_html_prolog($)
{
local *HANDLE;
- open(HANDLE, "<".$filename)
+ open(HANDLE, "<", $filename)
or die("ERROR: cannot open html prolog $filename!\n");
while (<HANDLE>)
{
@@ -5581,7 +5890,7 @@ sub get_html_prolog($)
<html lang="en">
<head>
- <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+ <meta http-equiv="Content-Type" content="text/html; charset=$charset">
<title>\@pagetitle\@</title>
<link rel="stylesheet" type="text/css" href="\@basedir\@gcov.css">
</head>
@@ -5611,7 +5920,7 @@ sub get_html_epilog($)
{
local *HANDLE;
- open(HANDLE, "<".$filename)
+ open(HANDLE, "<", $filename)
or die("ERROR: cannot open html epilog $filename!\n");
while (<HANDLE>)
{
@@ -5646,3 +5955,96 @@ sub die_handler($)
die("$tool_name: $msg");
}
+
+#
+# parse_ignore_errors(@ignore_errors)
+#
+# Parse user input about which errors to ignore.
+#
+
+sub parse_ignore_errors(@)
+{
+ my (@ignore_errors) = @_;
+ my @items;
+ my $item;
+
+ return if (!@ignore_errors);
+
+ foreach $item (@ignore_errors) {
+ $item =~ s/\s//g;
+ if ($item =~ /,/) {
+ # Split and add comma-separated parameters
+ push(@items, split(/,/, $item));
+ } else {
+ # Add single parameter
+ push(@items, $item);
+ }
+ }
+ foreach $item (@items) {
+ my $item_id = $ERROR_ID{lc($item)};
+
+ if (!defined($item_id)) {
+ die("ERROR: unknown argument for --ignore-errors: ".
+ "$item\n");
+ }
+ $ignore[$item_id] = 1;
+ }
+}
+
+#
+# parse_dir_prefix(@dir_prefix)
+#
+# Parse user input about the prefix list
+#
+
+sub parse_dir_prefix(@)
+{
+ my (@opt_dir_prefix) = @_;
+ my $item;
+
+ return if (!@opt_dir_prefix);
+
+ foreach $item (@opt_dir_prefix) {
+ if ($item =~ /,/) {
+ # Split and add comma-separated parameters
+ push(@dir_prefix, split(/,/, $item));
+ } else {
+ # Add single parameter
+ push(@dir_prefix, $item);
+ }
+ }
+}
+
+#
+# rate(hit, found[, suffix, precision, width])
+#
+# Return the coverage rate [0..100] for HIT and FOUND values. 0 is only
+# returned when HIT is 0. 100 is only returned when HIT equals FOUND.
+# PRECISION specifies the precision of the result. SUFFIX defines a
+# string that is appended to the result if FOUND is non-zero. Spaces
+# are added to the start of the resulting string until it is at least WIDTH
+# characters wide.
+#
+
+sub rate($$;$$$)
+{
+ my ($hit, $found, $suffix, $precision, $width) = @_;
+ my $rate;
+
+ # Assign defaults if necessary
+ $precision = $default_precision if (!defined($precision));
+ $suffix = "" if (!defined($suffix));
+ $width = 0 if (!defined($width));
+
+ return sprintf("%*s", $width, "-") if (!defined($found) || $found == 0);
+ $rate = sprintf("%.*f", $precision, $hit * 100 / $found);
+
+ # Adjust rates if necessary
+ if ($rate == 0 && $hit > 0) {
+ $rate = sprintf("%.*f", $precision, 1 / 10 ** $precision);
+ } elsif ($rate == 100 && $hit != $found) {
+ $rate = sprintf("%.*f", $precision, 100 - 1 / 10 ** $precision);
+ }
+
+ return sprintf("%*s", $width, $rate.$suffix);
+}
diff --git a/3rdParty/LCov/geninfo b/3rdParty/LCov/geninfo
index dcb1a67..7c4e6cc 100755
--- a/3rdParty/LCov/geninfo
+++ b/3rdParty/LCov/geninfo
@@ -1,6 +1,6 @@
#!/usr/bin/perl -w
#
-# Copyright (c) International Business Machines Corp., 2002,2010
+# Copyright (c) International Business Machines Corp., 2002,2012
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -52,17 +52,23 @@
use strict;
use File::Basename;
use File::Spec::Functions qw /abs2rel catdir file_name_is_absolute splitdir
- splitpath/;
+ splitpath catpath/;
use Getopt::Long;
use Digest::MD5 qw(md5_base64);
-
+use Cwd qw/abs_path/;
+if( $^O eq "msys" )
+{
+ require File::Spec::Win32;
+}
# Constants
-our $lcov_version = 'LCOV version 1.9';
+our $tool_dir = abs_path(dirname($0));
+our $lcov_version = "LCOV version 1.12";
our $lcov_url = "http://ltp.sourceforge.net/coverage/lcov.php";
our $gcov_tool = "gcov";
our $tool_name = basename($0);
+our $GCOV_VERSION_4_7_0 = 0x40700;
our $GCOV_VERSION_3_4_0 = 0x30400;
our $GCOV_VERSION_3_3_0 = 0x30300;
our $GCNO_FUNCTION_TAG = 0x01000000;
@@ -70,15 +76,68 @@ our $GCNO_LINES_TAG = 0x01450000;
our $GCNO_FILE_MAGIC = 0x67636e6f;
our $BBG_FILE_MAGIC = 0x67626267;
-our $COMPAT_HAMMER = "hammer";
-
+# Error classes which users may specify to ignore during processing
our $ERROR_GCOV = 0;
our $ERROR_SOURCE = 1;
our $ERROR_GRAPH = 2;
+our %ERROR_ID = (
+ "gcov" => $ERROR_GCOV,
+ "source" => $ERROR_SOURCE,
+ "graph" => $ERROR_GRAPH,
+);
our $EXCL_START = "LCOV_EXCL_START";
our $EXCL_STOP = "LCOV_EXCL_STOP";
-our $EXCL_LINE = "LCOV_EXCL_LINE";
+
+# Marker to exclude branch coverage but keep function and line coveage
+our $EXCL_BR_START = "LCOV_EXCL_BR_START";
+our $EXCL_BR_STOP = "LCOV_EXCL_BR_STOP";
+
+# Compatibility mode values
+our $COMPAT_VALUE_OFF = 0;
+our $COMPAT_VALUE_ON = 1;
+our $COMPAT_VALUE_AUTO = 2;
+
+# Compatibility mode value names
+our %COMPAT_NAME_TO_VALUE = (
+ "off" => $COMPAT_VALUE_OFF,
+ "on" => $COMPAT_VALUE_ON,
+ "auto" => $COMPAT_VALUE_AUTO,
+);
+
+# Compatiblity modes
+our $COMPAT_MODE_LIBTOOL = 1 << 0;
+our $COMPAT_MODE_HAMMER = 1 << 1;
+our $COMPAT_MODE_SPLIT_CRC = 1 << 2;
+
+# Compatibility mode names
+our %COMPAT_NAME_TO_MODE = (
+ "libtool" => $COMPAT_MODE_LIBTOOL,
+ "hammer" => $COMPAT_MODE_HAMMER,
+ "split_crc" => $COMPAT_MODE_SPLIT_CRC,
+ "android_4_4_0" => $COMPAT_MODE_SPLIT_CRC,
+);
+
+# Map modes to names
+our %COMPAT_MODE_TO_NAME = (
+ $COMPAT_MODE_LIBTOOL => "libtool",
+ $COMPAT_MODE_HAMMER => "hammer",
+ $COMPAT_MODE_SPLIT_CRC => "split_crc",
+);
+
+# Compatibility mode default values
+our %COMPAT_MODE_DEFAULTS = (
+ $COMPAT_MODE_LIBTOOL => $COMPAT_VALUE_ON,
+ $COMPAT_MODE_HAMMER => $COMPAT_VALUE_AUTO,
+ $COMPAT_MODE_SPLIT_CRC => $COMPAT_VALUE_AUTO,
+);
+
+# Compatibility mode auto-detection routines
+sub compat_hammer_autodetect();
+our %COMPAT_MODE_AUTO = (
+ $COMPAT_MODE_HAMMER => \&compat_hammer_autodetect,
+ $COMPAT_MODE_SPLIT_CRC => 1, # will be done later
+);
our $BR_LINE = 0;
our $BR_BLOCK = 1;
@@ -86,8 +145,9 @@ our $BR_BRANCH = 2;
our $BR_TAKEN = 3;
our $BR_VEC_ENTRIES = 4;
our $BR_VEC_WIDTH = 32;
+our $BR_VEC_MAX = vec(pack('b*', 1 x $BR_VEC_WIDTH), 0, $BR_VEC_WIDTH);
-our $UNNAMED_BLOCK = 9999;
+our $UNNAMED_BLOCK = -1;
# Prototypes
sub print_usage(*);
@@ -112,8 +172,9 @@ sub warn_handler($);
sub die_handler($);
sub graph_error($$);
sub graph_expect($);
-sub graph_read(*$;$);
+sub graph_read(*$;$$);
sub graph_skip(*$;$);
+sub uniq(@);
sub sort_uniq(@);
sub sort_uniq_lex(@);
sub graph_cleanup($);
@@ -123,18 +184,19 @@ sub graph_add_order($$$);
sub read_bb_word(*;$);
sub read_bb_value(*;$);
sub read_bb_string(*$);
-sub read_bb($$);
+sub read_bb($);
sub read_bbg_word(*;$);
sub read_bbg_value(*;$);
sub read_bbg_string(*);
-sub read_bbg_lines_record(*$$$$$$);
-sub read_bbg($$);
-sub read_gcno_word(*;$);
-sub read_gcno_value(*$;$);
+sub read_bbg_lines_record(*$$$$$);
+sub read_bbg($);
+sub read_gcno_word(*;$$);
+sub read_gcno_value(*$;$$);
sub read_gcno_string(*$);
-sub read_gcno_lines_record(*$$$$$$$);
+sub read_gcno_lines_record(*$$$$$$);
+sub determine_gcno_split_crc($$$);
sub read_gcno_function_record(*$$$$);
-sub read_gcno($$);
+sub read_gcno($);
sub get_gcov_capabilities();
sub get_overall_line($$$$);
sub print_overall_rate($$$$$$$$$);
@@ -142,10 +204,17 @@ sub br_gvec_len($);
sub br_gvec_get($$);
sub debug($);
sub int_handler();
+sub parse_ignore_errors(@);
+sub is_external($);
+sub compat_name($);
+sub parse_compat_modes($);
+sub is_compat($);
+sub is_compat_auto($);
# Global variables
our $gcov_version;
+our $gcov_version_string;
our $graph_file_extension;
our $data_file_extension;
our @data_directory;
@@ -158,12 +227,13 @@ our $version;
our $follow;
our $checksum;
our $no_checksum;
-our $compat_libtool;
-our $no_compat_libtool;
+our $opt_compat_libtool;
+our $opt_no_compat_libtool;
+our $rc_adjust_src_path;# Regexp specifying parts to remove from source path
+our $adjust_src_pattern;
+our $adjust_src_replace;
our $adjust_testname;
our $config; # Configuration file contents
-our $compatibility; # Compatibility version flag - used to indicate
- # non-standard GCOV data format versions
our @ignore_errors; # List of errors to ignore (parameter)
our @ignore; # List of errors to ignore (array)
our $initial;
@@ -171,9 +241,23 @@ our $no_recursion = 0;
our $maxdepth;
our $no_markers = 0;
our $opt_derive_func_data = 0;
+our $opt_external = 1;
+our $opt_no_external;
our $debug = 0;
our $gcov_caps;
our @gcov_options;
+our @internal_dirs;
+our $opt_config_file;
+our $opt_gcov_all_blocks = 1;
+our $opt_compat;
+our %opt_rc;
+our %compat_value;
+our $gcno_split_crc;
+our $func_coverage = 1;
+our $br_coverage = 0;
+our $rc_auto_base = 1;
+our $excl_line = "LCOV_EXCL_LINE";
+our $excl_br_line = "LCOV_EXCL_BR_LINE";
our $cwd = `pwd`;
chomp($cwd);
@@ -188,14 +272,32 @@ $SIG{"INT"} = \&int_handler;
$SIG{__WARN__} = \&warn_handler;
$SIG{__DIE__} = \&die_handler;
-# Prettify version string
-$lcov_version =~ s/\$\s*Revision\s*:?\s*(\S+)\s*\$/$1/;
+# Set LC_ALL so that gcov output will be in a unified format
+$ENV{"LC_ALL"} = "C";
-# Set LANG so that gcov output will be in a unified format
-$ENV{"LANG"} = "C";
+# Check command line for a configuration file name
+Getopt::Long::Configure("pass_through", "no_auto_abbrev");
+GetOptions("config-file=s" => \$opt_config_file,
+ "rc=s%" => \%opt_rc);
+Getopt::Long::Configure("default");
+
+{
+ # Remove spaces around rc options
+ my %new_opt_rc;
+
+ while (my ($key, $value) = each(%opt_rc)) {
+ $key =~ s/^\s+|\s+$//g;
+ $value =~ s/^\s+|\s+$//g;
+
+ $new_opt_rc{$key} = $value;
+ }
+ %opt_rc = %new_opt_rc;
+}
# Read configuration file if available
-if (defined($ENV{"HOME"}) && (-r $ENV{"HOME"}."/.lcovrc"))
+if (defined($opt_config_file)) {
+ $config = read_config($opt_config_file);
+} elsif (defined($ENV{"HOME"}) && (-r $ENV{"HOME"}."/.lcovrc"))
{
$config = read_config($ENV{"HOME"}."/.lcovrc");
}
@@ -204,15 +306,25 @@ elsif (-r "/etc/lcovrc")
$config = read_config("/etc/lcovrc");
}
-if ($config)
+if ($config || %opt_rc)
{
- # Copy configuration file values to variables
+ # Copy configuration file and --rc values to variables
apply_config({
"geninfo_gcov_tool" => \$gcov_tool,
"geninfo_adjust_testname" => \$adjust_testname,
"geninfo_checksum" => \$checksum,
"geninfo_no_checksum" => \$no_checksum, # deprecated
- "geninfo_compat_libtool" => \$compat_libtool});
+ "geninfo_compat_libtool" => \$opt_compat_libtool,
+ "geninfo_external" => \$opt_external,
+ "geninfo_gcov_all_blocks" => \$opt_gcov_all_blocks,
+ "geninfo_compat" => \$opt_compat,
+ "geninfo_adjust_src_path" => \$rc_adjust_src_path,
+ "geninfo_auto_base" => \$rc_auto_base,
+ "lcov_function_coverage" => \$func_coverage,
+ "lcov_branch_coverage" => \$br_coverage,
+ "lcov_excl_line" => \$excl_line,
+ "lcov_excl_br_line" => \$excl_br_line,
+ });
# Merge options
if (defined($no_checksum))
@@ -220,6 +332,34 @@ if ($config)
$checksum = ($no_checksum ? 0 : 1);
$no_checksum = undef;
}
+
+ # Check regexp
+ if (defined($rc_adjust_src_path)) {
+ my ($pattern, $replace) = split(/\s*=>\s*/,
+ $rc_adjust_src_path);
+ local $SIG{__DIE__};
+ eval '$adjust_src_pattern = qr>'.$pattern.'>;';
+ if (!defined($adjust_src_pattern)) {
+ my $msg = $@;
+
+ chomp($msg);
+ $msg =~ s/at \(eval.*$//;
+ warn("WARNING: invalid pattern in ".
+ "geninfo_adjust_src_path: $msg\n");
+ } elsif (!defined($replace)) {
+ # If no replacement is specified, simply remove pattern
+ $adjust_src_replace = "";
+ } else {
+ $adjust_src_replace = $replace;
+ }
+ }
+ for my $regexp (($excl_line, $excl_br_line)) {
+ eval 'qr/'.$regexp.'/';
+ my $error = $@;
+ chomp($error);
+ $error =~ s/at \(eval.*$//;
+ die("ERROR: invalid exclude pattern: $error") if $error;
+ }
}
# Parse command line options
@@ -232,8 +372,8 @@ if (!GetOptions("test-name|t=s" => \$test_name,
"quiet|q" => \$quiet,
"help|h|?" => \$help,
"follow|f" => \$follow,
- "compat-libtool" => \$compat_libtool,
- "no-compat-libtool" => \$no_compat_libtool,
+ "compat-libtool" => \$opt_compat_libtool,
+ "no-compat-libtool" => \$opt_no_compat_libtool,
"gcov-tool=s" => \$gcov_tool,
"ignore-errors=s" => \@ignore_errors,
"initial|i" => \$initial,
@@ -241,6 +381,11 @@ if (!GetOptions("test-name|t=s" => \$test_name,
"no-markers" => \$no_markers,
"derive-func-data" => \$opt_derive_func_data,
"debug" => \$debug,
+ "external" => \$opt_external,
+ "no-external" => \$opt_no_external,
+ "compat=s" => \$opt_compat,
+ "config-file=s" => \$opt_config_file,
+ "rc=s%" => \%opt_rc,
))
{
print(STDERR "Use $tool_name --help to get usage information\n");
@@ -255,10 +400,15 @@ else
$no_checksum = undef;
}
- if (defined($no_compat_libtool))
+ if (defined($opt_no_compat_libtool))
{
- $compat_libtool = ($no_compat_libtool ? 0 : 1);
- $no_compat_libtool = undef;
+ $opt_compat_libtool = ($opt_no_compat_libtool ? 0 : 1);
+ $opt_no_compat_libtool = undef;
+ }
+
+ if (defined($opt_no_external)) {
+ $opt_external = 0;
+ $opt_no_external = undef;
}
}
@@ -278,6 +428,30 @@ if ($version)
exit(0);
}
+# Check gcov tool
+if (system_no_output(3, $gcov_tool, "--help") == -1)
+{
+ die("ERROR: need tool $gcov_tool!\n");
+}
+
+($gcov_version, $gcov_version_string) = get_gcov_version();
+
+# Determine gcov options
+$gcov_caps = get_gcov_capabilities();
+push(@gcov_options, "-b") if ($gcov_caps->{'branch-probabilities'} &&
+ ($br_coverage || $func_coverage));
+push(@gcov_options, "-c") if ($gcov_caps->{'branch-counts'} &&
+ $br_coverage);
+push(@gcov_options, "-a") if ($gcov_caps->{'all-blocks'} &&
+ $opt_gcov_all_blocks && $br_coverage);
+push(@gcov_options, "-p") if ($gcov_caps->{'preserve-paths'});
+
+# Determine compatibility modes
+parse_compat_modes($opt_compat);
+
+# Determine which errors the user wants us to ignore
+parse_ignore_errors(@ignore_errors);
+
# Make sure test names only contain valid characters
if ($test_name =~ s/\W/_/g)
{
@@ -319,17 +493,6 @@ else
$checksum = 0;
}
-# Determine libtool compatibility mode
-if (defined($compat_libtool))
-{
- $compat_libtool = ($compat_libtool? 1 : 0);
-}
-else
-{
- # Default is on
- $compat_libtool = 1;
-}
-
# Determine max depth for recursion
if ($no_recursion)
{
@@ -358,42 +521,9 @@ else
}
}
-if (@ignore_errors)
-{
- my @expanded;
- my $error;
-
- # Expand comma-separated entries
- foreach (@ignore_errors) {
- if (/,/)
- {
- push(@expanded, split(",", $_));
- }
- else
- {
- push(@expanded, $_);
- }
- }
-
- foreach (@expanded)
- {
- /^gcov$/ && do { $ignore[$ERROR_GCOV] = 1; next; } ;
- /^source$/ && do { $ignore[$ERROR_SOURCE] = 1; next; };
- /^graph$/ && do { $ignore[$ERROR_GRAPH] = 1; next; };
- die("ERROR: unknown argument for --ignore-errors: $_\n");
- }
-}
-
-if (system_no_output(3, $gcov_tool, "--help") == -1)
-{
- die("ERROR: need tool $gcov_tool!\n");
-}
-
-$gcov_version = get_gcov_version();
-
if ($gcov_version < $GCOV_VERSION_3_4_0)
{
- if (defined($compatibility) && $compatibility eq $COMPAT_HAMMER)
+ if (is_compat($COMPAT_MODE_HAMMER))
{
$data_file_extension = ".da";
$graph_file_extension = ".bbg";
@@ -410,20 +540,13 @@ else
$graph_file_extension = ".gcno";
}
-# Determine gcov options
-$gcov_caps = get_gcov_capabilities();
-push(@gcov_options, "-b") if ($gcov_caps->{'branch-probabilities'});
-push(@gcov_options, "-c") if ($gcov_caps->{'branch-counts'});
-push(@gcov_options, "-a") if ($gcov_caps->{'all-blocks'});
-push(@gcov_options, "-p") if ($gcov_caps->{'preserve-paths'});
-
# Check output filename
if (defined($output_filename) && ($output_filename ne "-"))
{
# Initially create output filename, data is appended
# for each data file processed
local *DUMMY_HANDLE;
- open(DUMMY_HANDLE, ">$output_filename")
+ open(DUMMY_HANDLE, ">", $output_filename)
or die("ERROR: cannot create $output_filename!\n");
close(DUMMY_HANDLE);
@@ -435,12 +558,18 @@ if (defined($output_filename) && ($output_filename ne "-"))
}
}
+# Build list of directories to identify external files
+foreach my $entry(@data_directory, $base_directory) {
+ next if (!defined($entry));
+ push(@internal_dirs, solve_relative_path($cwd, $entry));
+}
+
# Do something
foreach my $entry (@data_directory) {
gen_info($entry);
}
-if ($initial) {
+if ($initial && $br_coverage) {
warn("Note: --initial does not generate branch coverage ".
"data\n");
}
@@ -480,9 +609,12 @@ sequentially.
--gcov-tool TOOL Specify gcov tool location
--ignore-errors ERROR Continue after ERROR (gcov, source, graph)
--no-recursion Exclude subdirectories from processing
- --function-coverage Capture function call counts
--no-markers Ignore exclusion markers in source code
--derive-func-data Generate function data from line data
+ --(no-)external Include (ignore) data for external files
+ --config-file FILENAME Specify configuration file location
+ --rc SETTING=VALUE Override configuration file setting
+ --compat MODE=on|off|auto Set compat MODE (libtool, hammer, split_crc)
For more information see: $lcov_url
END_OF_USAGE
@@ -578,10 +710,13 @@ sub gen_info($)
{
info("Scanning $directory for $ext files ...\n");
- @file_list = `find "$directory" $maxdepth $follow -name \\*$ext -type f 2>/dev/null`;
+ @file_list = `find "$directory" $maxdepth $follow -name \\*$ext -type f -o -name \\*$ext -type l 2>/dev/null`;
chomp(@file_list);
- @file_list or
- die("ERROR: no $ext files found in $directory!\n");
+ if (!@file_list) {
+ warn("WARNING: no $ext files found in $directory - ".
+ "skipping!\n");
+ return;
+ }
$prefix = get_common_prefix(1, @file_list);
info("Found %d %s files in %s\n", $#file_list+1, $type,
$directory);
@@ -604,6 +739,25 @@ sub gen_info($)
}
+#
+# derive_data(contentdata, funcdata, bbdata)
+#
+# Calculate function coverage data by combining line coverage data and the
+# list of lines belonging to a function.
+#
+# contentdata: [ instr1, count1, source1, instr2, count2, source2, ... ]
+# instr<n>: Instrumentation flag for line n
+# count<n>: Execution count for line n
+# source<n>: Source code for line n
+#
+# funcdata: [ count1, func1, count2, func2, ... ]
+# count<n>: Execution count for function number n
+# func<n>: Function name for function number n
+#
+# bbdata: function_name -> [ line1, line2, ... ]
+# line<n>: Line number belonging to the corresponding function
+#
+
sub derive_data($$$)
{
my ($contentdata, $funcdata, $bbdata) = @_;
@@ -633,6 +787,7 @@ sub derive_data($$$)
foreach $fn (keys(%{$bbdata})) {
my $line_data = $bbdata->{$fn};
my $line;
+ my $fninstr = 0;
if ($fn eq "") {
next;
@@ -640,13 +795,17 @@ sub derive_data($$$)
# Find the lowest line count for this function
$count = 0;
foreach $line (@$line_data) {
+ my $linstr = $gcov_content[ ( $line - 1 ) * 3 + 0 ];
my $lcount = $gcov_content[ ( $line - 1 ) * 3 + 1 ];
+ next if (!$linstr);
+ $fninstr = 1;
if (($lcount > 0) &&
(($count == 0) || ($lcount < $count))) {
$count = $lcount;
}
}
+ next if (!$fninstr);
$fn_count{$fn} = $count;
}
@@ -733,7 +892,6 @@ sub process_dafile($$)
my $source; # gcov source header information
my $object; # gcov object header information
my @matches; # List of absolute paths matching filename
- my @unprocessed; # List of unprocessed source code files
my $base_dir; # Base directory for current file
my @tmp_links; # Temporary links to be cleaned up
my @result;
@@ -749,11 +907,10 @@ sub process_dafile($$)
# Get directory and basename of data file
($da_dir, $da_basename) = split_filename($da_filename);
- # avoid files from .libs dirs
- if ($compat_libtool && $da_dir =~ m/(.*)\/\.libs$/) {
- $source_dir = $1;
- } else {
- $source_dir = $da_dir;
+ $source_dir = $da_dir;
+ if (is_compat($COMPAT_MODE_LIBTOOL)) {
+ # Avoid files from .libs dirs
+ $source_dir =~ s/\.libs$//;
}
if (-z $da_filename)
@@ -808,20 +965,27 @@ sub process_dafile($$)
# information about functions and their source code positions.
if ($gcov_version < $GCOV_VERSION_3_4_0)
{
- if (defined($compatibility) && $compatibility eq $COMPAT_HAMMER)
+ if (is_compat($COMPAT_MODE_HAMMER))
{
- ($instr, $graph) = read_bbg($bb_filename, $base_dir);
+ ($instr, $graph) = read_bbg($bb_filename);
}
else
{
- ($instr, $graph) = read_bb($bb_filename, $base_dir);
+ ($instr, $graph) = read_bb($bb_filename);
}
}
else
{
- ($instr, $graph) = read_gcno($bb_filename, $base_dir);
+ ($instr, $graph) = read_gcno($bb_filename);
}
+ # Try to find base directory automatically if requested by user
+ if ($rc_auto_base) {
+ $base_dir = find_base_from_graph($base_dir, $instr, $graph);
+ }
+
+ ($instr, $graph) = adjust_graph_filenames($base_dir, $instr, $graph);
+
# Set $object_dir to real location of object files. This may differ
# from $da_dir if the graph file is just a link to the "real" object
# file location.
@@ -850,6 +1014,7 @@ sub process_dafile($$)
}
# Change to directory containing data files and apply GCOV
+ debug("chdir($base_dir)\n");
chdir($base_dir);
if ($da_renamed)
@@ -905,7 +1070,7 @@ sub process_dafile($$)
else
{
# Append to output file
- open(INFO_HANDLE, ">>$output_filename")
+ open(INFO_HANDLE, ">>", $output_filename)
or die("ERROR: cannot write to ".
"$output_filename!\n");
}
@@ -913,7 +1078,7 @@ sub process_dafile($$)
else
{
# Open .info file for output
- open(INFO_HANDLE, ">$da_filename.info")
+ open(INFO_HANDLE, ">", "$da_filename.info")
or die("ERROR: cannot create $da_filename.info!\n");
}
@@ -922,23 +1087,34 @@ sub process_dafile($$)
# Traverse the list of generated .gcov files and combine them into a
# single .info file
- @unprocessed = keys(%{$instr});
foreach $gcov_file (sort(@gcov_list))
{
my $i;
my $num;
+ # Skip gcov file for gcc built-in code
+ next if ($gcov_file eq "<built-in>.gcov");
+
($source, $object) = read_gcov_header($gcov_file);
- if (defined($source))
- {
- $source = solve_relative_path($base_dir, $source);
+ if (!defined($source)) {
+ # Derive source file name from gcov file name if
+ # header format could not be parsed
+ $source = $gcov_file;
+ $source =~ s/\.gcov$//;
+ }
+
+ $source = solve_relative_path($base_dir, $source);
+
+ if (defined($adjust_src_pattern)) {
+ # Apply transformation as specified by user
+ $source =~ s/$adjust_src_pattern/$adjust_src_replace/g;
}
# gcov will happily create output even if there's no source code
# available - this interferes with checksum creation so we need
# to pull the emergency brake here.
- if (defined($source) && ! -r $source && $checksum)
+ if (! -r $source && $checksum)
{
if ($ignore[$ERROR_SOURCE])
{
@@ -949,8 +1125,7 @@ sub process_dafile($$)
die("ERROR: could not read source file $source\n");
}
- @matches = match_filename(defined($source) ? $source :
- $gcov_file, keys(%{$instr}));
+ @matches = match_filename($source, keys(%{$instr}));
# Skip files that are not mentioned in the graph file
if (!@matches)
@@ -994,13 +1169,13 @@ sub process_dafile($$)
\@matches, \@gcov_content);
}
- # Remove processed file from list
- for ($index = scalar(@unprocessed) - 1; $index >= 0; $index--)
- {
- if ($unprocessed[$index] eq $source_filename)
- {
- splice(@unprocessed, $index, 1);
- last;
+ # Skip external files if requested
+ if (!$opt_external) {
+ if (is_external($source_filename)) {
+ info(" ignoring data for external file ".
+ "$source_filename\n");
+ unlink($gcov_file);
+ next;
}
}
@@ -1090,6 +1265,7 @@ sub process_dafile($$)
my ($line, $block, $branch, $taken) =
br_gvec_get($gcov_branches, $i);
+ $block = $BR_VEC_MAX if ($block < 0);
print(INFO_HANDLE "BRDA:$line,$block,$branch,$taken\n");
$br_found++;
$br_hit++ if ($taken ne '-' && $taken > 0);
@@ -1138,16 +1314,6 @@ sub process_dafile($$)
unlink($gcov_file);
}
- # Check for files which show up in the graph file but were never
- # processed
- if (@unprocessed && @gcov_list)
- {
- foreach (@unprocessed)
- {
- warn("WARNING: no data found for $_\n");
- }
- }
-
if (!($output_filename && ($output_filename eq "-")))
{
close(INFO_HANDLE);
@@ -1168,8 +1334,40 @@ sub solve_relative_path($$)
{
my $path = $_[0];
my $dir = $_[1];
+ my $volume;
+ my $directories;
+ my $filename;
+ my @dirs; # holds path elements
my $result;
+ # Convert from Windows path to msys path
+ if( $^O eq "msys" )
+ {
+ # search for a windows drive letter at the beginning
+ ($volume, $directories, $filename) = File::Spec::Win32->splitpath( $dir );
+ if( $volume ne '' )
+ {
+ my $uppercase_volume;
+ # transform c/d\../e/f\g to Windows style c\d\..\e\f\g
+ $dir = File::Spec::Win32->canonpath( $dir );
+ # use Win32 module to retrieve path components
+ # $uppercase_volume is not used any further
+ ( $uppercase_volume, $directories, $filename ) = File::Spec::Win32->splitpath( $dir );
+ @dirs = File::Spec::Win32->splitdir( $directories );
+
+ # prepend volume, since in msys C: is always mounted to /c
+ $volume =~ s|^([a-zA-Z]+):|/\L$1\E|;
+ unshift( @dirs, $volume );
+
+ # transform to Unix style '/' path
+ $directories = File::Spec->catdir( @dirs );
+ $dir = File::Spec->catpath( '', $directories, $filename );
+ } else {
+ # eliminate '\' path separators
+ $dir = File::Spec->canonpath( $dir );
+ }
+ }
+
$result = $dir;
# Prepend path if not absolute
if ($dir =~ /^[^\/]/)
@@ -1182,6 +1380,10 @@ sub solve_relative_path($$)
# Remove .
$result =~ s/\/\.\//\//g;
+ $result =~ s/\/\.$/\//g;
+
+ # Remove trailing /
+ $result =~ s/\/$//g;
# Solve ..
while ($result =~ s/\/[^\/]+\/\.\.\//\//)
@@ -1266,7 +1468,7 @@ sub solve_ambiguous_match($$$)
{
# Compare file contents
- open(SOURCE, $filename)
+ open(SOURCE, "<", $filename)
or die("ERROR: cannot read $filename!\n");
$no_match = 0;
@@ -1336,7 +1538,7 @@ sub read_gcov_header($)
my $object;
local *INPUT;
- if (!open(INPUT, $_[0]))
+ if (!open(INPUT, "<", $_[0]))
{
if ($ignore_errors[$ERROR_GCOV])
{
@@ -1408,6 +1610,7 @@ sub br_gvec_get($$)
# Retrieve data from vector
$line = vec($vec, $offset + $BR_LINE, $BR_VEC_WIDTH);
$block = vec($vec, $offset + $BR_BLOCK, $BR_VEC_WIDTH);
+ $block = -1 if ($block == $BR_VEC_MAX);
$branch = vec($vec, $offset + $BR_BRANCH, $BR_VEC_WIDTH);
$taken = vec($vec, $offset + $BR_TAKEN, $BR_VEC_WIDTH);
@@ -1435,6 +1638,7 @@ sub br_gvec_push($$$$$)
$vec = "" if (!defined($vec));
$offset = br_gvec_len($vec) * $BR_VEC_ENTRIES;
+ $block = $BR_VEC_MAX if $block < 0;
# Encode taken value into an integer
if ($taken eq "-") {
@@ -1484,11 +1688,13 @@ sub read_gcov_file($)
my $number;
my $exclude_flag = 0;
my $exclude_line = 0;
+ my $exclude_br_flag = 0;
+ my $exclude_branch = 0;
my $last_block = $UNNAMED_BLOCK;
my $last_line = 0;
local *INPUT;
- if (!open(INPUT, $filename)) {
+ if (!open(INPUT, "<", $filename)) {
if ($ignore_errors[$ERROR_GCOV])
{
warn("WARNING: cannot read $filename!\n");
@@ -1508,11 +1714,15 @@ sub read_gcov_file($)
s/\015$//;
if (/^branch\s+(\d+)\s+taken\s+=\s+(\d+)/) {
+ next if (!$br_coverage);
next if ($exclude_line);
+ next if ($exclude_branch);
$branches = br_gvec_push($branches, $last_line,
$last_block, $1, $2);
} elsif (/^branch\s+(\d+)\s+never\s+executed/) {
+ next if (!$br_coverage);
next if ($exclude_line);
+ next if ($exclude_branch);
$branches = br_gvec_push($branches, $last_line,
$last_block, $1, '-');
}
@@ -1530,12 +1740,25 @@ sub read_gcov_file($)
} elsif (/$EXCL_START/) {
$exclude_flag = 1;
}
- if (/$EXCL_LINE/ || $exclude_flag) {
+ if (/$excl_line/ || $exclude_flag) {
$exclude_line = 1;
} else {
$exclude_line = 0;
}
}
+ # Check for exclusion markers (branch exclude)
+ if (!$no_markers) {
+ if (/$EXCL_BR_STOP/) {
+ $exclude_br_flag = 0;
+ } elsif (/$EXCL_BR_START/) {
+ $exclude_br_flag = 1;
+ }
+ if (/$excl_br_line/ || $exclude_br_flag) {
+ $exclude_branch = 1;
+ } else {
+ $exclude_branch = 0;
+ }
+ }
# Source code execution data
if (/^\t\t(.*)$/)
{
@@ -1579,16 +1802,21 @@ sub read_gcov_file($)
$last_line = $2;
$last_block = $3;
} elsif (/^branch\s+(\d+)\s+taken\s+(\d+)/) {
+ next if (!$br_coverage);
next if ($exclude_line);
+ next if ($exclude_branch);
$branches = br_gvec_push($branches, $last_line,
$last_block, $1, $2);
} elsif (/^branch\s+(\d+)\s+never\s+executed/) {
+ next if (!$br_coverage);
next if ($exclude_line);
+ next if ($exclude_branch);
$branches = br_gvec_push($branches, $last_line,
$last_block, $1, '-');
}
- elsif (/^function\s+(\S+)\s+called\s+(\d+)/)
+ elsif (/^function\s+(.+)\s+called\s+(\d+)\s+/)
{
+ next if (!$func_coverage);
if ($exclude_line) {
next;
}
@@ -1611,12 +1839,26 @@ sub read_gcov_file($)
} elsif (/$EXCL_START/) {
$exclude_flag = 1;
}
- if (/$EXCL_LINE/ || $exclude_flag) {
+ if (/$excl_line/ || $exclude_flag) {
$exclude_line = 1;
} else {
$exclude_line = 0;
}
}
+ # Check for exclusion markers (branch exclude)
+ if (!$no_markers) {
+ if (/$EXCL_BR_STOP/) {
+ $exclude_br_flag = 0;
+ } elsif (/$EXCL_BR_START/) {
+ $exclude_br_flag = 1;
+ }
+ if (/$excl_br_line/ || $exclude_br_flag) {
+ $exclude_branch = 1;
+ } else {
+ $exclude_branch = 0;
+ }
+ }
+
# <exec count>:<line number>:<source code>
if ($line eq "0")
{
@@ -1636,7 +1878,7 @@ sub read_gcov_file($)
push(@result, 0);
} else {
# Check for zero count
- if ($count eq "#####") {
+ if ($count =~ /^[#=]/) {
$count = 0;
}
push(@result, 1);
@@ -1649,7 +1891,7 @@ sub read_gcov_file($)
}
close(INPUT);
- if ($exclude_flag) {
+ if ($exclude_flag || $exclude_br_flag) {
warn("WARNING: unterminated exclusion section in $filename\n");
}
return(\@result, $branches, \@functions);
@@ -1667,12 +1909,29 @@ sub get_gcov_version()
local *HANDLE;
my $version_string;
my $result;
+ my $pipe_next_line;
- open(GCOV_PIPE, "$gcov_tool -v |")
+ open(GCOV_PIPE, "-|", "$gcov_tool --version")
or die("ERROR: cannot retrieve gcov version!\n");
$version_string = <GCOV_PIPE>;
+ # LLVM gcov keeps version information on the second line.
+ # For example, gcov --version yields:
+ # LLVM (http://llvm.org/):
+ # LLVM version 3.4svn
+
+ $pipe_next_line = <GCOV_PIPE>;
+ # In case version information is on first line.
+ # For example, with Xcode 7.0 gcov --version yields:
+ # Apple LLVM 7.0.0 (clang-700.0.65)
+
+ $version_string = $pipe_next_line if ($pipe_next_line && $version_string =~ /LLVM/);
close(GCOV_PIPE);
+ # Remove version information in parenthesis to cope with the following:
+ # - gcov (GCC) 4.4.7 20120313 (Red Hat 4.4.7-3)
+ # - gcov (crosstool-NG 1.18.0) 4.7.2
+ $version_string =~ s/\([^\)]*\)//g;
+
$result = 0;
if ($version_string =~ /(\d+)\.(\d+)(\.(\d+))?/)
{
@@ -1687,13 +1946,22 @@ sub get_gcov_version()
$result = $1 << 16 | $2 << 8;
}
}
- if ($version_string =~ /suse/i && $result == 0x30303 ||
- $version_string =~ /mandrake/i && $result == 0x30302)
+ if ($version_string =~ /LLVM/)
{
- info("Using compatibility mode for GCC 3.3 (hammer)\n");
- $compatibility = $COMPAT_HAMMER;
+ # Map LLVM versions to the version of GCC gcov which
+ # they emulate
+ if ($result >= 0x030400)
+ {
+ info("Found LLVM gcov version 3.4, which emulates gcov version 4.2\n");
+ $result = 0x040200;
+ }
+ else
+ {
+ warn("This version of LLVM's gcov is unknown. Assuming it emulates GCC gcov version 4.2.\n");
+ $result = 0x040200;
+ }
}
- return $result;
+ return ($result, $version_string);
}
@@ -1756,13 +2024,14 @@ sub system_no_output($@)
local *OLD_STDOUT;
# Save old stdout and stderr handles
- ($mode & 1) && open(OLD_STDOUT, ">>&STDOUT");
- ($mode & 2) && open(OLD_STDERR, ">>&STDERR");
+ ($mode & 1) && open(OLD_STDOUT, ">>&", "STDOUT");
+ ($mode & 2) && open(OLD_STDERR, ">>&", "STDERR");
# Redirect to /dev/null
- ($mode & 1) && open(STDOUT, ">/dev/null");
- ($mode & 2) && open(STDERR, ">/dev/null");
+ ($mode & 1) && open(STDOUT, ">", "/dev/null");
+ ($mode & 2) && open(STDERR, ">", "/dev/null");
+ debug("system(".join(' ', @_).")\n");
system(@_);
$result = $?;
@@ -1771,8 +2040,8 @@ sub system_no_output($@)
($mode & 2) && close(STDERR);
# Restore old handles
- ($mode & 1) && open(STDOUT, ">>&OLD_STDOUT");
- ($mode & 2) && open(STDERR, ">>&OLD_STDERR");
+ ($mode & 1) && open(STDOUT, ">>&", "OLD_STDOUT");
+ ($mode & 2) && open(STDERR, ">>&", "OLD_STDERR");
return $result;
}
@@ -1793,7 +2062,7 @@ sub read_config($)
my $value;
local *HANDLE;
- if (!open(HANDLE, "<$filename"))
+ if (!open(HANDLE, "<", $filename))
{
warn("WARNING: cannot read configuration file $filename\n");
return undef;
@@ -1832,8 +2101,8 @@ sub read_config($)
# key_string => var_ref
#
# where KEY_STRING is a keyword and VAR_REF is a reference to an associated
-# variable. If the global configuration hash CONFIG contains a value for
-# keyword KEY_STRING, VAR_REF will be assigned the value for that keyword.
+# variable. If the global configuration hashes CONFIG or OPT_RC contain a value
+# for keyword KEY_STRING, VAR_REF will be assigned the value for that keyword.
#
sub apply_config($)
@@ -1842,8 +2111,9 @@ sub apply_config($)
foreach (keys(%{$ref}))
{
- if (defined($config->{$_}))
- {
+ if (defined($opt_rc{$_})) {
+ ${$ref->{$_}} = $opt_rc{$_};
+ } elsif (defined($config->{$_})) {
${$ref->{$_}} = $config->{$_};
}
}
@@ -1865,7 +2135,7 @@ sub get_exclusion_data($)
my $flag = 0;
local *HANDLE;
- if (!open(HANDLE, "<$filename")) {
+ if (!open(HANDLE, "<", $filename)) {
warn("WARNING: could not open $filename\n");
return undef;
}
@@ -1875,7 +2145,7 @@ sub get_exclusion_data($)
} elsif (/$EXCL_START/) {
$flag = 1;
}
- if (/$EXCL_LINE/ || $flag) {
+ if (/$excl_line/ || $flag) {
$list{$.} = 1;
}
}
@@ -1989,12 +2259,7 @@ sub apply_exclusion_data($$)
}
# Store modified list
- if (scalar(@new_data) > 0) {
- $instr->{$filename} = \@new_data;
- } else {
- # All of this file was excluded
- delete($instr->{$filename});
- }
+ $instr->{$filename} = \@new_data;
}
return ($instr, $graph);
@@ -2023,11 +2288,10 @@ sub process_graphfile($$)
# Get directory and basename of data file
($graph_dir, $graph_basename) = split_filename($graph_filename);
- # avoid files from .libs dirs
- if ($compat_libtool && $graph_dir =~ m/(.*)\/\.libs$/) {
- $source_dir = $1;
- } else {
- $source_dir = $graph_dir;
+ $source_dir = $graph_dir;
+ if (is_compat($COMPAT_MODE_LIBTOOL)) {
+ # Avoid files from .libs dirs
+ $source_dir =~ s/\.libs$//;
}
# Construct base_dir for current file
@@ -2040,22 +2304,36 @@ sub process_graphfile($$)
$base_dir = $source_dir;
}
+ # Ignore empty graph file (e.g. source file with no statement)
+ if (-z $graph_filename)
+ {
+ warn("WARNING: empty $graph_filename (skipped)\n");
+ return;
+ }
+
if ($gcov_version < $GCOV_VERSION_3_4_0)
{
- if (defined($compatibility) && $compatibility eq $COMPAT_HAMMER)
+ if (is_compat($COMPAT_MODE_HAMMER))
{
- ($instr, $graph) = read_bbg($graph_filename, $base_dir);
+ ($instr, $graph) = read_bbg($graph_filename);
}
else
{
- ($instr, $graph) = read_bb($graph_filename, $base_dir);
+ ($instr, $graph) = read_bb($graph_filename);
}
}
else
{
- ($instr, $graph) = read_gcno($graph_filename, $base_dir);
+ ($instr, $graph) = read_gcno($graph_filename);
}
+ # Try to find base directory automatically if requested by user
+ if ($rc_auto_base) {
+ $base_dir = find_base_from_graph($base_dir, $instr, $graph);
+ }
+
+ ($instr, $graph) = adjust_graph_filenames($base_dir, $instr, $graph);
+
if (!$no_markers) {
# Apply exclusion marker data to graph file data
($instr, $graph) = apply_exclusion_data($instr, $graph);
@@ -2071,7 +2349,7 @@ sub process_graphfile($$)
else
{
# Append to output file
- open(INFO_HANDLE, ">>$output_filename")
+ open(INFO_HANDLE, ">>", $output_filename)
or die("ERROR: cannot write to ".
"$output_filename!\n");
}
@@ -2079,7 +2357,7 @@ sub process_graphfile($$)
else
{
# Open .info file for output
- open(INFO_HANDLE, ">$graph_filename.info")
+ open(INFO_HANDLE, ">", "$graph_filename.info")
or die("ERROR: cannot create $graph_filename.info!\n");
}
@@ -2091,9 +2369,18 @@ sub process_graphfile($$)
my $line;
my $linedata;
+ # Skip external files if requested
+ if (!$opt_external) {
+ if (is_external($filename)) {
+ info(" ignoring data for external file ".
+ "$filename\n");
+ next;
+ }
+ }
+
print(INFO_HANDLE "SF:$filename\n");
- if (defined($funcdata)) {
+ if (defined($funcdata) && $func_coverage) {
my @functions = sort {$funcdata->{$a}->[0] <=>
$funcdata->{$b}->[0]}
keys(%{$funcdata});
@@ -2194,26 +2481,36 @@ sub graph_expect($)
}
#
-# graph_read(handle, bytes[, description])
+# graph_read(handle, bytes[, description, peek])
#
# Read and return the specified number of bytes from handle. Return undef
-# if the number of bytes could not be read.
+# if the number of bytes could not be read. If PEEK is non-zero, reset
+# file position after read.
#
-sub graph_read(*$;$)
+sub graph_read(*$;$$)
{
- my ($handle, $length, $desc) = @_;
+ my ($handle, $length, $desc, $peek) = @_;
my $data;
my $result;
+ my $pos;
graph_expect($desc);
+ if ($peek) {
+ $pos = tell($handle);
+ if ($pos == -1) {
+ warn("Could not get current file position: $!\n");
+ return undef;
+ }
+ }
$result = read($handle, $data, $length);
if ($debug) {
+ my $op = $peek ? "peek" : "read";
my $ascii = "";
my $hex = "";
my $i;
- print(STDERR "DEBUG: read($length)=$result: ");
+ print(STDERR "DEBUG: $op($length)=$result: ");
for ($i = 0; $i < length($data); $i++) {
my $c = substr($data, $i, 1);;
my $n = ord($c);
@@ -2228,6 +2525,12 @@ sub graph_read(*$;$)
print(STDERR "$hex |$ascii|");
print(STDERR "\n");
}
+ if ($peek) {
+ if (!seek($handle, $pos, 0)) {
+ warn("Could not set file position: $!\n");
+ return undef;
+ }
+ }
if ($result != $length) {
return undef;
}
@@ -2252,6 +2555,27 @@ sub graph_skip(*$;$)
}
#
+# uniq(list)
+#
+# Return list without duplicate entries.
+#
+
+sub uniq(@)
+{
+ my (@list) = @_;
+ my @new_list;
+ my %known;
+
+ foreach my $item (@list) {
+ next if ($known{$item});
+ $known{$item} = 1;
+ push(@new_list, $item);
+ }
+
+ return @new_list;
+}
+
+#
# sort_uniq(list)
#
# Return list in numerically ascending order and without duplicate entries.
@@ -2286,6 +2610,133 @@ sub sort_uniq_lex(@)
}
#
+# parent_dir(dir)
+#
+# Return parent directory for DIR. DIR must not contain relative path
+# components.
+#
+
+sub parent_dir($)
+{
+ my ($dir) = @_;
+ my ($v, $d, $f) = splitpath($dir, 1);
+ my @dirs = splitdir($d);
+
+ pop(@dirs);
+
+ return catpath($v, catdir(@dirs), $f);
+}
+
+#
+# find_base_from_graph(base_dir, instr, graph)
+#
+# Try to determine the base directory of the graph file specified by INSTR
+# and GRAPH. The base directory is the base for all relative filenames in
+# the graph file. It is defined by the current working directory at time
+# of compiling the source file.
+#
+# This function implements a heuristic which relies on the following
+# assumptions:
+# - all files used for compilation are still present at their location
+# - the base directory is either BASE_DIR or one of its parent directories
+# - files by the same name are not present in multiple parent directories
+#
+
+sub find_base_from_graph($$$)
+{
+ my ($base_dir, $instr, $graph) = @_;
+ my $old_base;
+ my $best_miss;
+ my $best_base;
+ my %rel_files;
+
+ # Determine list of relative paths
+ foreach my $filename (keys(%{$instr}), keys(%{$graph})) {
+ next if (file_name_is_absolute($filename));
+
+ $rel_files{$filename} = 1;
+ }
+
+ # Early exit if there are no relative paths
+ return $base_dir if (!%rel_files);
+
+ do {
+ my $miss = 0;
+
+ foreach my $filename (keys(%rel_files)) {
+ if (!-e solve_relative_path($base_dir, $filename)) {
+ $miss++;
+ }
+ }
+
+ debug("base_dir=$base_dir miss=$miss\n");
+
+ # Exit if we find an exact match with no misses
+ return $base_dir if ($miss == 0);
+
+ # No exact match, aim for the one with the least source file
+ # misses
+ if (!defined($best_base) || $miss < $best_miss) {
+ $best_base = $base_dir;
+ $best_miss = $miss;
+ }
+
+ # Repeat until there's no more parent directory
+ $old_base = $base_dir;
+ $base_dir = parent_dir($base_dir);
+ } while ($old_base ne $base_dir);
+
+ return $best_base;
+}
+
+#
+# adjust_graph_filenames(base_dir, instr, graph)
+#
+# Make relative paths in INSTR and GRAPH absolute and apply
+# geninfo_adjust_src_path setting to graph file data.
+#
+
+sub adjust_graph_filenames($$$)
+{
+ my ($base_dir, $instr, $graph) = @_;
+
+ foreach my $filename (keys(%{$instr})) {
+ my $old_filename = $filename;
+
+ # Convert to absolute canonical form
+ $filename = solve_relative_path($base_dir, $filename);
+
+ # Apply adjustment
+ if (defined($adjust_src_pattern)) {
+ $filename =~ s/$adjust_src_pattern/$adjust_src_replace/g;
+ }
+
+ if ($filename ne $old_filename) {
+ $instr->{$filename} = delete($instr->{$old_filename});
+ }
+ }
+
+ foreach my $filename (keys(%{$graph})) {
+ my $old_filename = $filename;
+
+ # Make absolute
+ # Convert to absolute canonical form
+ $filename = solve_relative_path($base_dir, $filename);
+
+ # Apply adjustment
+ if (defined($adjust_src_pattern)) {
+ $filename =~ s/$adjust_src_pattern/$adjust_src_replace/g;
+ }
+
+ if ($filename ne $old_filename) {
+ $graph->{$filename} = delete($graph->{$old_filename});
+ }
+ }
+
+ return ($instr, $graph);
+}
+
+#
# graph_cleanup(graph)
#
# Remove entries for functions with no lines. Remove duplicate line numbers.
@@ -2310,7 +2761,7 @@ sub graph_cleanup($)
next;
}
# Normalize list
- $per_file->{$function} = [ sort_uniq(@$lines) ];
+ $per_file->{$function} = [ uniq(@$lines) ];
}
if (scalar(keys(%{$per_file})) == 0) {
# Remove empty file
@@ -2454,6 +2905,7 @@ sub graph_add_order($$$)
push(@$list, $filename);
$fileorder->{$function} = $list;
}
+
#
# read_bb_word(handle[, description])
#
@@ -2513,7 +2965,7 @@ sub read_bb_string(*$)
}
#
-# read_bb(filename, base_dir)
+# read_bb(filename)
#
# Read the contents of the specified .bb file and return (instr, graph), where:
#
@@ -2524,14 +2976,12 @@ sub read_bb_string(*$)
# file_data : function name -> line_data
# line_data : [ line1, line2, ... ]
#
-# Relative filenames are converted to absolute form using base_dir as
-# base directory. See the gcov info pages of gcc 2.95 for a description of
-# the .bb file format.
+# See the gcov info pages of gcc 2.95 for a description of the .bb file format.
#
-sub read_bb($$)
+sub read_bb($)
{
- my ($bb_filename, $base) = @_;
+ my ($bb_filename) = @_;
my $minus_one = 0x80000001;
my $minus_two = 0x80000002;
my $value;
@@ -2543,7 +2993,7 @@ sub read_bb($$)
my $graph;
local *HANDLE;
- open(HANDLE, "<$bb_filename") or goto open_error;
+ open(HANDLE, "<", $bb_filename) or goto open_error;
binmode(HANDLE);
while (!eof(HANDLE)) {
$value = read_bb_value(*HANDLE, "data word");
@@ -2553,10 +3003,6 @@ sub read_bb($$)
graph_expect("filename");
$filename = read_bb_string(*HANDLE, $minus_one);
goto incomplete if (!defined($filename));
- if ($filename ne "") {
- $filename = solve_relative_path($base,
- $filename);
- }
} elsif ($value == $minus_two) {
# Function name
graph_expect("function name");
@@ -2647,16 +3093,15 @@ sub read_bbg_string(*)
#
# read_bbg_lines_record(handle, bbg_filename, bb, fileorder, filename,
-# function, base)
+# function)
#
# Read a bbg format lines record from handle and add the relevant data to
# bb and fileorder. Return filename on success, undef on error.
#
-sub read_bbg_lines_record(*$$$$$$)
+sub read_bbg_lines_record(*$$$$$)
{
- my ($handle, $bbg_filename, $bb, $fileorder, $filename, $function,
- $base) = @_;
+ my ($handle, $bbg_filename, $bb, $fileorder, $filename, $function) = @_;
my $string;
my $lineno;
@@ -2676,7 +3121,10 @@ sub read_bbg_lines_record(*$$$$$$)
if ($string eq "") {
return $filename;
}
- $filename = solve_relative_path($base, $string);
+ $filename = $string;
+ if (!exists($bb->{$function}->{$filename})) {
+ $bb->{$function}->{$filename} = [];
+ }
next;
}
# Got an actual line number
@@ -2691,21 +3139,20 @@ sub read_bbg_lines_record(*$$$$$$)
}
#
-# read_bbg(filename, base_dir)
+# read_bbg(filename)
#
# Read the contents of the specified .bbg file and return the following mapping:
# graph: filename -> file_data
# file_data: function name -> line_data
# line_data: [ line1, line2, ... ]
#
-# Relative filenames are converted to absolute form using base_dir as
-# base directory. See the gcov-io.h file in the SLES 9 gcc 3.3.3 source code
-# for a description of the .bbg format.
+# See the gcov-io.h file in the SLES 9 gcc 3.3.3 source code for a description
+# of the .bbg format.
#
-sub read_bbg($$)
+sub read_bbg($)
{
- my ($bbg_filename, $base) = @_;
+ my ($bbg_filename) = @_;
my $file_magic = 0x67626267;
my $tag_function = 0x01000000;
my $tag_lines = 0x01450000;
@@ -2720,7 +3167,7 @@ sub read_bbg($$)
my $graph;
local *HANDLE;
- open(HANDLE, "<$bbg_filename") or goto open_error;
+ open(HANDLE, "<", $bbg_filename) or goto open_error;
binmode(HANDLE);
# Read magic
$word = read_bbg_value(*HANDLE, "file magic");
@@ -2752,7 +3199,7 @@ sub read_bbg($$)
# Read lines record
$filename = read_bbg_lines_record(HANDLE, $bbg_filename,
$bb, $fileorder, $filename,
- $function, $base);
+ $function);
goto incomplete if (!defined($filename));
} else {
# Skip record contents
@@ -2778,31 +3225,33 @@ magic_error:
}
#
-# read_gcno_word(handle[, description])
+# read_gcno_word(handle[, description, peek])
#
# Read and return a word in .gcno format.
#
-sub read_gcno_word(*;$)
+sub read_gcno_word(*;$$)
{
- my ($handle, $desc) = @_;
+ my ($handle, $desc, $peek) = @_;
- return graph_read($handle, 4, $desc);
+ return graph_read($handle, 4, $desc, $peek);
}
#
-# read_gcno_value(handle, big_endian[, description])
+# read_gcno_value(handle, big_endian[, description, peek])
#
# Read a word in .gcno format from handle and return its integer value
-# according to the specified endianness.
+# according to the specified endianness. If PEEK is non-zero, reset file
+# position after read.
#
-sub read_gcno_value(*$;$)
+sub read_gcno_value(*$;$$)
{
- my ($handle, $big_endian, $desc) = @_;
+ my ($handle, $big_endian, $desc, $peek) = @_;
my $word;
+ my $pos;
- $word = read_gcno_word($handle, $desc);
+ $word = read_gcno_word($handle, $desc, $peek);
return undef if (!defined($word));
if ($big_endian) {
return unpack("N", $word);
@@ -2841,16 +3290,16 @@ sub read_gcno_string(*$)
#
# read_gcno_lines_record(handle, gcno_filename, bb, fileorder, filename,
-# function, base, big_endian)
+# function, big_endian)
#
# Read a gcno format lines record from handle and add the relevant data to
# bb and fileorder. Return filename on success, undef on error.
#
-sub read_gcno_lines_record(*$$$$$$$)
+sub read_gcno_lines_record(*$$$$$$)
{
my ($handle, $gcno_filename, $bb, $fileorder, $filename, $function,
- $base, $big_endian) = @_;
+ $big_endian) = @_;
my $string;
my $lineno;
@@ -2870,7 +3319,10 @@ sub read_gcno_lines_record(*$$$$$$$)
if ($string eq "") {
return $filename;
}
- $filename = solve_relative_path($base, $string);
+ $filename = $string;
+ if (!exists($bb->{$function}->{$filename})) {
+ $bb->{$function}->{$filename} = [];
+ }
next;
}
# Got an actual line number
@@ -2886,7 +3338,52 @@ sub read_gcno_lines_record(*$$$$$$$)
}
#
-# read_gcno_function_record(handle, graph, base, big_endian)
+# determine_gcno_split_crc(handle, big_endian, rec_length)
+#
+# Determine if HANDLE refers to a .gcno file with a split checksum function
+# record format. Return non-zero in case of split checksum format, zero
+# otherwise, undef in case of read error.
+#
+
+sub determine_gcno_split_crc($$$)
+{
+ my ($handle, $big_endian, $rec_length) = @_;
+ my $strlen;
+ my $overlong_string;
+
+ return 1 if ($gcov_version >= $GCOV_VERSION_4_7_0);
+ return 1 if (is_compat($COMPAT_MODE_SPLIT_CRC));
+
+ # Heuristic:
+ # Decide format based on contents of next word in record:
+ # - pre-gcc 4.7
+ # This is the function name length / 4 which should be
+ # less than the remaining record length
+ # - gcc 4.7
+ # This is a checksum, likely with high-order bits set,
+ # resulting in a large number
+ $strlen = read_gcno_value($handle, $big_endian, undef, 1);
+ return undef if (!defined($strlen));
+ $overlong_string = 1 if ($strlen * 4 >= $rec_length - 12);
+
+ if ($overlong_string) {
+ if (is_compat_auto($COMPAT_MODE_SPLIT_CRC)) {
+ info("Auto-detected compatibility mode for split ".
+ "checksum .gcno file format\n");
+
+ return 1;
+ } else {
+ # Sanity check
+ warn("Found overlong string in function record: ".
+ "try '--compat split_crc'\n");
+ }
+ }
+
+ return 0;
+}
+
+#
+# read_gcno_function_record(handle, graph, big_endian, rec_length)
#
# Read a gcno format function record from handle and add the relevant data
# to graph. Return (filename, function) on success, undef on error.
@@ -2894,7 +3391,7 @@ sub read_gcno_lines_record(*$$$$$$$)
sub read_gcno_function_record(*$$$$)
{
- my ($handle, $bb, $fileorder, $base, $big_endian) = @_;
+ my ($handle, $bb, $fileorder, $big_endian, $rec_length) = @_;
my $filename;
my $function;
my $lineno;
@@ -2903,6 +3400,14 @@ sub read_gcno_function_record(*$$$$)
graph_expect("function record");
# Skip ident and checksum
graph_skip($handle, 8, "function ident and checksum") or return undef;
+ # Determine if this is a function record with split checksums
+ if (!defined($gcno_split_crc)) {
+ $gcno_split_crc = determine_gcno_split_crc($handle, $big_endian,
+ $rec_length);
+ return undef if (!defined($gcno_split_crc));
+ }
+ # Skip cfg checksum word in case of split checksums
+ graph_skip($handle, 4, "function cfg checksum") if ($gcno_split_crc);
# Read function name
graph_expect("function name");
$function = read_gcno_string($handle, $big_endian);
@@ -2911,7 +3416,6 @@ sub read_gcno_function_record(*$$$$)
graph_expect("filename");
$filename = read_gcno_string($handle, $big_endian);
return undef if (!defined($filename));
- $filename = solve_relative_path($base, $filename);
# Read first line number
$lineno = read_gcno_value($handle, $big_endian, "initial line number");
return undef if (!defined($lineno));
@@ -2923,7 +3427,7 @@ sub read_gcno_function_record(*$$$$)
}
#
-# read_gcno(filename, base_dir)
+# read_gcno(filename)
#
# Read the contents of the specified .gcno file and return the following
# mapping:
@@ -2931,14 +3435,13 @@ sub read_gcno_function_record(*$$$$)
# file_data: function name -> line_data
# line_data: [ line1, line2, ... ]
#
-# Relative filenames are converted to absolute form using base_dir as
-# base directory. See the gcov-io.h file in the gcc 3.3 source code
-# for a description of the .gcno format.
+# See the gcov-io.h file in the gcc 3.3 source code for a description of
+# the .gcno format.
#
-sub read_gcno($$)
+sub read_gcno($)
{
- my ($gcno_filename, $base) = @_;
+ my ($gcno_filename) = @_;
my $file_magic = 0x67636e6f;
my $tag_function = 0x01000000;
my $tag_lines = 0x01450000;
@@ -2952,9 +3455,11 @@ sub read_gcno($$)
my $fileorder = {};
my $instr;
my $graph;
+ my $filelength;
local *HANDLE;
- open(HANDLE, "<$gcno_filename") or goto open_error;
+ open(HANDLE, "<", $gcno_filename) or goto open_error;
+ $filelength = (stat(HANDLE))[7];
binmode(HANDLE);
# Read magic
$word = read_gcno_word(*HANDLE, "file magic");
@@ -2986,16 +3491,25 @@ sub read_gcno($$)
$next_pos = tell(HANDLE);
goto tell_error if ($next_pos == -1);
$next_pos += $length;
+ # Catch garbage at the end of a gcno file
+ if ($next_pos > $filelength) {
+ debug("Overlong record: file_length=$filelength ".
+ "rec_length=$length\n");
+ warn("WARNING: $gcno_filename: Overlong record at end ".
+ "of file!\n");
+ last;
+ }
# Process record
if ($tag == $tag_function) {
($filename, $function) = read_gcno_function_record(
- *HANDLE, $bb, $fileorder, $base, $big_endian);
+ *HANDLE, $bb, $fileorder, $big_endian,
+ $length);
goto incomplete if (!defined($function));
} elsif ($tag == $tag_lines) {
# Read lines record
$filename = read_gcno_lines_record(*HANDLE,
$gcno_filename, $bb, $fileorder,
- $filename, $function, $base,
+ $filename, $function,
$big_endian);
goto incomplete if (!defined($filename));
} else {
@@ -3053,16 +3567,236 @@ sub get_gcov_capabilities()
{
my $help = `$gcov_tool --help`;
my %capabilities;
+ my %short_option_translations = (
+ 'a' => 'all-blocks',
+ 'b' => 'branch-probabilities',
+ 'c' => 'branch-counts',
+ 'f' => 'function-summaries',
+ 'h' => 'help',
+ 'l' => 'long-file-names',
+ 'n' => 'no-output',
+ 'o' => 'object-directory',
+ 'p' => 'preserve-paths',
+ 'u' => 'unconditional-branches',
+ 'v' => 'version',
+ );
foreach (split(/\n/, $help)) {
- next if (!/--(\S+)/);
- next if ($1 eq 'help');
- next if ($1 eq 'version');
- next if ($1 eq 'object-directory');
+ my $capability;
+ if (/--(\S+)/) {
+ $capability = $1;
+ } else {
+ # If the line provides a short option, translate it.
+ next if (!/^\s*-(\S)\s/);
+ $capability = $short_option_translations{$1};
+ next if not defined($capability);
+ }
+ next if ($capability eq 'help');
+ next if ($capability eq 'version');
+ next if ($capability eq 'object-directory');
- $capabilities{$1} = 1;
- debug("gcov has capability '$1'\n");
+ $capabilities{$capability} = 1;
+ debug("gcov has capability '$capability'\n");
}
return \%capabilities;
}
+
+#
+# parse_ignore_errors(@ignore_errors)
+#
+# Parse user input about which errors to ignore.
+#
+
+sub parse_ignore_errors(@)
+{
+ my (@ignore_errors) = @_;
+ my @items;
+ my $item;
+
+ return if (!@ignore_errors);
+
+ foreach $item (@ignore_errors) {
+ $item =~ s/\s//g;
+ if ($item =~ /,/) {
+ # Split and add comma-separated parameters
+ push(@items, split(/,/, $item));
+ } else {
+ # Add single parameter
+ push(@items, $item);
+ }
+ }
+ foreach $item (@items) {
+ my $item_id = $ERROR_ID{lc($item)};
+
+ if (!defined($item_id)) {
+ die("ERROR: unknown argument for --ignore-errors: ".
+ "$item\n");
+ }
+ $ignore[$item_id] = 1;
+ }
+}
+
+#
+# is_external(filename)
+#
+# Determine if a file is located outside of the specified data directories.
+#
+
+sub is_external($)
+{
+ my ($filename) = @_;
+ my $dir;
+
+ foreach $dir (@internal_dirs) {
+ return 0 if ($filename =~ /^\Q$dir\/\E/);
+ }
+ return 1;
+}
+
+#
+# compat_name(mode)
+#
+# Return the name of compatibility mode MODE.
+#
+
+sub compat_name($)
+{
+ my ($mode) = @_;
+ my $name = $COMPAT_MODE_TO_NAME{$mode};
+
+ return $name if (defined($name));
+
+ return "<unknown>";
+}
+
+#
+# parse_compat_modes(opt)
+#
+# Determine compatibility mode settings.
+#
+
+sub parse_compat_modes($)
+{
+ my ($opt) = @_;
+ my @opt_list;
+ my %specified;
+
+ # Initialize with defaults
+ %compat_value = %COMPAT_MODE_DEFAULTS;
+
+ # Add old style specifications
+ if (defined($opt_compat_libtool)) {
+ $compat_value{$COMPAT_MODE_LIBTOOL} =
+ $opt_compat_libtool ? $COMPAT_VALUE_ON
+ : $COMPAT_VALUE_OFF;
+ }
+
+ # Parse settings
+ if (defined($opt)) {
+ @opt_list = split(/\s*,\s*/, $opt);
+ }
+ foreach my $directive (@opt_list) {
+ my ($mode, $value);
+
+ # Either
+ # mode=off|on|auto or
+ # mode (implies on)
+ if ($directive !~ /^(\w+)=(\w+)$/ &&
+ $directive !~ /^(\w+)$/) {
+ die("ERROR: Unknown compatibility mode specification: ".
+ "$directive!\n");
+ }
+ # Determine mode
+ $mode = $COMPAT_NAME_TO_MODE{lc($1)};
+ if (!defined($mode)) {
+ die("ERROR: Unknown compatibility mode '$1'!\n");
+ }
+ $specified{$mode} = 1;
+ # Determine value
+ if (defined($2)) {
+ $value = $COMPAT_NAME_TO_VALUE{lc($2)};
+ if (!defined($value)) {
+ die("ERROR: Unknown compatibility mode ".
+ "value '$2'!\n");
+ }
+ } else {
+ $value = $COMPAT_VALUE_ON;
+ }
+ $compat_value{$mode} = $value;
+ }
+ # Perform auto-detection
+ foreach my $mode (sort(keys(%compat_value))) {
+ my $value = $compat_value{$mode};
+ my $is_autodetect = "";
+ my $name = compat_name($mode);
+
+ if ($value == $COMPAT_VALUE_AUTO) {
+ my $autodetect = $COMPAT_MODE_AUTO{$mode};
+
+ if (!defined($autodetect)) {
+ die("ERROR: No auto-detection for ".
+ "mode '$name' available!\n");
+ }
+
+ if (ref($autodetect) eq "CODE") {
+ $value = &$autodetect();
+ $compat_value{$mode} = $value;
+ $is_autodetect = " (auto-detected)";
+ }
+ }
+
+ if ($specified{$mode}) {
+ if ($value == $COMPAT_VALUE_ON) {
+ info("Enabling compatibility mode ".
+ "'$name'$is_autodetect\n");
+ } elsif ($value == $COMPAT_VALUE_OFF) {
+ info("Disabling compatibility mode ".
+ "'$name'$is_autodetect\n");
+ } else {
+ info("Using delayed auto-detection for ".
+ "compatibility mode ".
+ "'$name'\n");
+ }
+ }
+ }
+}
+
+sub compat_hammer_autodetect()
+{
+ if ($gcov_version_string =~ /suse/i && $gcov_version == 0x30303 ||
+ $gcov_version_string =~ /mandrake/i && $gcov_version == 0x30302)
+ {
+ info("Auto-detected compatibility mode for GCC 3.3 (hammer)\n");
+ return $COMPAT_VALUE_ON;
+ }
+ return $COMPAT_VALUE_OFF;
+}
+
+#
+# is_compat(mode)
+#
+# Return non-zero if compatibility mode MODE is enabled.
+#
+
+sub is_compat($)
+{
+ my ($mode) = @_;
+
+ return 1 if ($compat_value{$mode} == $COMPAT_VALUE_ON);
+ return 0;
+}
+
+#
+# is_compat_auto(mode)
+#
+# Return non-zero if compatibility mode MODE is set to auto-detect.
+#
+
+sub is_compat_auto($)
+{
+ my ($mode) = @_;
+
+ return 1 if ($compat_value{$mode} == $COMPAT_VALUE_AUTO);
+ return 0;
+}
diff --git a/3rdParty/LCov/genpng b/3rdParty/LCov/genpng
index 7fe9dfe..55e013e 100755
--- a/3rdParty/LCov/genpng
+++ b/3rdParty/LCov/genpng
@@ -22,7 +22,7 @@
# This script creates an overview PNG image of a source code file by
# representing each source code character by a single pixel.
#
-# Note that the PERL module GD.pm is required for this script to work.
+# Note that the Perl module GD.pm is required for this script to work.
# It may be obtained from http://www.cpan.org
#
# History:
@@ -32,10 +32,12 @@
use strict;
use File::Basename;
use Getopt::Long;
+use Cwd qw/abs_path/;
# Constants
-our $lcov_version = 'LCOV version 1.9';
+our $tool_dir = abs_path(dirname($0));
+our $lcov_version = "LCOV version 1.12";
our $lcov_url = "http://ltp.sourceforge.net/coverage/lcov.php";
our $tool_name = basename($0);
@@ -53,9 +55,6 @@ sub genpng_die_handler($);
# Code entry point
#
-# Prettify version string
-$lcov_version =~ s/\$\s*Revision\s*:?\s*(\S+)\s*\$/$1/;
-
# Check whether required module GD.pm is installed
if (check_and_load_module("GD"))
{
@@ -182,7 +181,7 @@ sub genpng_process_file($$$$)
local *HANDLE;
my @source;
- open(HANDLE, "<$filename")
+ open(HANDLE, "<", $filename)
or die("ERROR: cannot open $filename!\n");
# Check for .gcov filename extension
@@ -238,7 +237,7 @@ sub gen_png($$$@)
my $overview_width = shift(@_); # Imagewidth for image
my $tab_size = shift(@_); # Replacement string for tab signs
my @source = @_; # Source code as passed via argument 2
- my $height = scalar(@source); # Height as define by source size
+ my $height; # Height as define by source size
my $overview; # Source code overview image data
my $col_plain_back; # Color for overview background
my $col_plain_text; # Color for uninstrumented text
@@ -261,6 +260,11 @@ sub gen_png($$$@)
my $replacement; # Replacement string for tabulator chars
local *PNG_HANDLE; # Handle for output PNG file
+ # Handle empty source files
+ if (!@source) {
+ @source = ( "" );
+ }
+ $height = scalar(@source);
# Create image
$overview = new GD::Image($overview_width, $height)
or die("ERROR: cannot allocate overview image!\n");
@@ -362,7 +366,7 @@ sub gen_png($$$@)
}
# Write PNG file
- open (PNG_HANDLE, ">$filename")
+ open (PNG_HANDLE, ">", $filename)
or die("ERROR: cannot write png file $filename!\n");
binmode(*PNG_HANDLE);
print(PNG_HANDLE $overview->png());
diff --git a/3rdParty/LCov/lcov b/3rdParty/LCov/lcov
index 4e392ff..7760ba2 100755
--- a/3rdParty/LCov/lcov
+++ b/3rdParty/LCov/lcov
@@ -1,6 +1,6 @@
#!/usr/bin/perl -w
#
-# Copyright (c) International Business Machines Corp., 2002,2010
+# Copyright (c) International Business Machines Corp., 2002,2012
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -71,7 +71,8 @@ use Cwd qw /abs_path getcwd/;
# Global constants
-our $lcov_version = 'LCOV version 1.9';
+our $tool_dir = abs_path(dirname($0));
+our $lcov_version = "LCOV version 1.12";
our $lcov_url = "http://ltp.sourceforge.net/coverage/lcov.php";
our $tool_name = basename($0);
@@ -93,6 +94,7 @@ our $BR_BRANCH = 1;
our $BR_TAKEN = 2;
our $BR_VEC_ENTRIES = 3;
our $BR_VEC_WIDTH = 32;
+our $BR_VEC_MAX = vec(pack('b*', 1 x $BR_VEC_WIDTH), 0, $BR_VEC_WIDTH);
# Branch data combination types
our $BR_SUB = 0;
@@ -139,6 +141,8 @@ sub lcov_geninfo(@);
sub create_package($$$;$);
sub get_func_found_and_hit($);
sub br_ivec_get($$);
+sub summary();
+sub rate($$;$$$);
# Global variables & initialization
our @directory; # Specifies where to get coverage data from
@@ -168,7 +172,7 @@ our $no_checksum; # If set, don't calculate a checksum for each line
our $compat_libtool; # If set, indicates that libtool mode is to be enabled
our $no_compat_libtool; # If set, indicates that libtool mode is to be disabled
our $gcov_tool;
-our $ignore_errors;
+our @opt_ignore_errors;
our $initial;
our $no_recursion = 0;
our $to_package;
@@ -177,7 +181,6 @@ our $maxdepth;
our $no_markers;
our $config; # Configuration file contents
chomp($cwd);
-our $tool_dir = dirname($0); # Directory where genhtml tool is installed
our @temp_dirs;
our $gcov_gkv; # gcov kernel support version found on machine
our $opt_derive_func_data;
@@ -186,12 +189,20 @@ our $opt_list_full_path;
our $opt_no_list_full_path;
our $opt_list_width = 80;
our $opt_list_truncate_max = 20;
+our $opt_external;
+our $opt_no_external;
+our $opt_config_file;
+our %opt_rc;
+our @opt_summary;
+our $opt_compat;
our $ln_overall_found;
our $ln_overall_hit;
our $fn_overall_found;
our $fn_overall_hit;
our $br_overall_found;
our $br_overall_hit;
+our $func_coverage = 1;
+our $br_coverage = 0;
#
@@ -203,17 +214,29 @@ $SIG{__DIE__} = \&die_handler;
$SIG{'INT'} = \&abort_handler;
$SIG{'QUIT'} = \&abort_handler;
-# Prettify version string
-$lcov_version =~ s/\$\s*Revision\s*:?\s*(\S+)\s*\$/$1/;
+# Check command line for a configuration file name
+Getopt::Long::Configure("pass_through", "no_auto_abbrev");
+GetOptions("config-file=s" => \$opt_config_file,
+ "rc=s%" => \%opt_rc);
+Getopt::Long::Configure("default");
-# Add current working directory if $tool_dir is not already an absolute path
-if (! ($tool_dir =~ /^\/(.*)$/))
{
- $tool_dir = "$cwd/$tool_dir";
+ # Remove spaces around rc options
+ my %new_opt_rc;
+
+ while (my ($key, $value) = each(%opt_rc)) {
+ $key =~ s/^\s+|\s+$//g;
+ $value =~ s/^\s+|\s+$//g;
+
+ $new_opt_rc{$key} = $value;
+ }
+ %opt_rc = %new_opt_rc;
}
# Read configuration file if available
-if (defined($ENV{"HOME"}) && (-r $ENV{"HOME"}."/.lcovrc"))
+if (defined($opt_config_file)) {
+ $config = read_config($opt_config_file);
+} elsif (defined($ENV{"HOME"}) && (-r $ENV{"HOME"}."/.lcovrc"))
{
$config = read_config($ENV{"HOME"}."/.lcovrc");
}
@@ -222,15 +245,17 @@ elsif (-r "/etc/lcovrc")
$config = read_config("/etc/lcovrc");
}
-if ($config)
+if ($config || %opt_rc)
{
- # Copy configuration file values to variables
+ # Copy configuration file and --rc values to variables
apply_config({
"lcov_gcov_dir" => \$gcov_dir,
"lcov_tmp_dir" => \$tmp_dir,
"lcov_list_full_path" => \$opt_list_full_path,
"lcov_list_width" => \$opt_list_width,
"lcov_list_truncate_max"=> \$opt_list_truncate_max,
+ "lcov_branch_coverage" => \$br_coverage,
+ "lcov_function_coverage"=> \$func_coverage,
});
}
@@ -259,7 +284,7 @@ if (!GetOptions("directory|d|di=s" => \@directory,
"compat-libtool" => \$compat_libtool,
"no-compat-libtool" => \$no_compat_libtool,
"gcov-tool=s" => \$gcov_tool,
- "ignore-errors=s" => \$ignore_errors,
+ "ignore-errors=s" => \@opt_ignore_errors,
"initial|i" => \$initial,
"no-recursion" => \$no_recursion,
"to-package=s" => \$to_package,
@@ -269,6 +294,12 @@ if (!GetOptions("directory|d|di=s" => \@directory,
"debug" => \$opt_debug,
"list-full-path" => \$opt_list_full_path,
"no-list-full-path" => \$opt_no_list_full_path,
+ "external" => \$opt_external,
+ "no-external" => \$opt_no_external,
+ "summary=s" => \@opt_summary,
+ "compat=s" => \$opt_compat,
+ "config-file=s" => \$opt_config_file,
+ "rc=s%" => \%opt_rc,
))
{
print(STDERR "Use $tool_name --help to get usage information\n");
@@ -294,6 +325,11 @@ else
$opt_list_full_path = ($opt_no_list_full_path ? 0 : 1);
$opt_no_list_full_path = undef;
}
+
+ if (defined($opt_no_external)) {
+ $opt_external = 0;
+ $opt_no_external = undef;
+ }
}
# Check for help option
@@ -341,7 +377,7 @@ else
check_options();
# Only --extract, --remove and --diff allow unnamed parameters
-if (@ARGV && !($extract || $remove || $diff))
+if (@ARGV && !($extract || $remove || $diff || @opt_summary))
{
die("Extra parameter found: '".join(" ", @ARGV)."'\n".
"Use $tool_name --help to get usage information\n");
@@ -429,6 +465,12 @@ elsif ($diff)
$fn_overall_found, $fn_overall_hit,
$br_overall_found, $br_overall_hit) = diff();
}
+elsif (@opt_summary)
+{
+ ($ln_overall_found, $ln_overall_hit,
+ $fn_overall_found, $fn_overall_hit,
+ $br_overall_found, $br_overall_hit) = summary();
+}
temp_cleanup();
@@ -471,6 +513,7 @@ Operation:
-r, --remove FILE PATTERN Remove files matching PATTERN from FILE
-l, --list FILE List contents of tracefile FILE
--diff FILE DIFF Transform tracefile FILE according to DIFF
+ --summary FILE Show summary coverage data for tracefiles
Options:
-i, --initial Capture initial zero coverage data
@@ -493,6 +536,10 @@ Options:
--no-markers Ignore exclusion markers in source code
--derive-func-data Generate function data from line data
--list-full-path Print full path during a list operation
+ --(no-)external Include (ignore) data for external files
+ --config-file FILENAME Specify configuration file location
+ --rc SETTING=VALUE Override configuration file setting
+ --compat MODE=on|off|auto Set compat MODE (libtool, hammer, split_crc)
For more information see: $lcov_url
END_OF_USAGE
@@ -518,17 +565,18 @@ sub check_options()
$remove && $i++;
$list && $i++;
$diff && $i++;
+ @opt_summary && $i++;
if ($i == 0)
{
- die("Need one of the options -z, -c, -a, -e, -r, -l or ".
- "--diff\n".
+ die("Need one of options -z, -c, -a, -e, -r, -l, ".
+ "--diff or --summary\n".
"Use $tool_name --help to get usage information\n");
}
elsif ($i > 1)
{
- die("ERROR: only one of -z, -c, -a, -e, -r, -l or ".
- "--diff allowed!\n".
+ die("ERROR: only one of -z, -c, -a, -e, -r, -l, ".
+ "--diff or --summary allowed!\n".
"Use $tool_name --help to get usage information\n");
}
}
@@ -551,7 +599,7 @@ sub userspace_reset()
{
info("Deleting all .da files in $current_dir".
($no_recursion?"\n":" and subdirectories\n"));
- @file_list = `find "$current_dir" $maxdepth $follow -name \\*\\.da -o -name \\*\\.gcda -type f 2>/dev/null`;
+ @file_list = `find "$current_dir" $maxdepth $follow -name \\*\\.da -type f -o -name \\*\\.gcda -type f 2>/dev/null`;
chomp(@file_list);
foreach (@file_list)
{
@@ -613,7 +661,7 @@ sub kernel_reset()
} else {
die("ERROR: no reset control found in $gcov_dir\n");
}
- open(HANDLE, ">$reset_file") or
+ open(HANDLE, ">", $reset_file) or
die("ERROR: cannot write to $reset_file!\n");
print(HANDLE "0");
close(HANDLE);
@@ -635,10 +683,10 @@ sub lcov_copy_single($$)
local $/;
local *HANDLE;
- open(HANDLE, "<$from") or die("ERROR: cannot read $from: $!\n");
+ open(HANDLE, "<", $from) or die("ERROR: cannot read $from: $!\n");
$content = <HANDLE>;
close(HANDLE);
- open(HANDLE, ">$to") or die("ERROR: cannot write $from: $!\n");
+ open(HANDLE, ">", $to) or die("ERROR: cannot write $from: $!\n");
if (defined($content)) {
print(HANDLE $content);
}
@@ -798,9 +846,11 @@ sub lcov_geninfo(@)
{
@param = (@param, "--gcov-tool", $gcov_tool);
}
- if ($ignore_errors)
- {
- @param = (@param, "--ignore-errors", $ignore_errors);
+ foreach (@opt_ignore_errors) {
+ @param = (@param, "--ignore-errors", $_);
+ }
+ if ($no_recursion) {
+ @param = (@param, "--no-recursion");
}
if ($initial)
{
@@ -818,6 +868,26 @@ sub lcov_geninfo(@)
{
@param = (@param, "--debug");
}
+ if (defined($opt_external) && $opt_external)
+ {
+ @param = (@param, "--external");
+ }
+ if (defined($opt_external) && !$opt_external)
+ {
+ @param = (@param, "--no-external");
+ }
+ if (defined($opt_compat)) {
+ @param = (@param, "--compat", $opt_compat);
+ }
+ if (%opt_rc) {
+ foreach my $key (keys(%opt_rc)) {
+ @param = (@param, "--rc", "$key=".$opt_rc{$key});
+ }
+ }
+ if (defined($opt_config_file)) {
+ @param = (@param, "--config-file", $opt_config_file);
+ }
+
system(@param) and exit($? >> 8);
}
@@ -834,7 +904,7 @@ sub read_file($)
local $\;
local *HANDLE;
- open(HANDLE, "<$filename") || return undef;
+ open(HANDLE, "<", $filename) || return undef;
$content = <HANDLE>;
close(HANDLE);
@@ -860,17 +930,21 @@ sub get_package($)
local *HANDLE;
info("Reading package $file:\n");
- info(" data directory .......: $dir\n");
$file = abs_path($file);
chdir($dir);
- open(HANDLE, "tar xvfz $file 2>/dev/null|")
+ open(HANDLE, "-|", "tar xvfz '$file' 2>/dev/null")
or die("ERROR: could not process package $file\n");
+ $count = 0;
while (<HANDLE>) {
if (/\.da$/ || /\.gcda$/) {
$count++;
}
}
close(HANDLE);
+ if ($count == 0) {
+ die("ERROR: no data file found in package $file\n");
+ }
+ info(" data directory .......: $dir\n");
$build = read_file("$dir/$pkg_build_file");
if (defined($build)) {
info(" build directory ......: $build\n");
@@ -904,7 +978,7 @@ sub write_file($$)
my ($filename, $content) = @_;
local *HANDLE;
- open(HANDLE, ">$filename") || return 0;
+ open(HANDLE, ">", $filename) || return 0;
print(HANDLE $content);
close(HANDLE) || return 0;
@@ -922,7 +996,7 @@ sub count_package_data($)
local *HANDLE;
my $count = 0;
- open(HANDLE, "tar tfz $filename|") or return undef;
+ open(HANDLE, "-|", "tar tfz '$filename'") or return undef;
while (<HANDLE>) {
if (/\.da$/ || /\.gcda$/) {
$count++;
@@ -944,6 +1018,10 @@ sub create_package($$$;$)
my ($file, $dir, $build, $gkv) = @_;
my $cwd = getcwd();
+ # Check for availability of tar tool first
+ system("tar --help > /dev/null")
+ and die("ERROR: tar command not available\n");
+
# Print information about the package
info("Creating package $file:\n");
info(" data directory .......: $dir\n");
@@ -972,6 +1050,7 @@ sub create_package($$$;$)
chdir($dir);
system("tar cfz $file .")
and die("ERROR: could not create package $file\n");
+ chdir($cwd);
# Remove temporary files
unlink("$dir/$pkg_build_file");
@@ -985,7 +1064,6 @@ sub create_package($$$;$)
info(" data files ...........: $count\n");
}
}
- chdir($cwd);
}
sub find_link_fn($$$)
@@ -1208,6 +1286,113 @@ sub kernel_capture()
}
#
+# link_data_cb(datadir, rel, graphdir)
+#
+# Create symbolic link in GRAPDIR/REL pointing to DATADIR/REL.
+#
+
+sub link_data_cb($$$)
+{
+ my ($datadir, $rel, $graphdir) = @_;
+ my $absfrom = catfile($datadir, $rel);
+ my $absto = catfile($graphdir, $rel);
+ my $base;
+ my $dir;
+
+ if (-e $absto) {
+ die("ERROR: could not create symlink at $absto: ".
+ "File already exists!\n");
+ }
+ if (-l $absto) {
+ # Broken link - possibly from an interrupted earlier run
+ unlink($absto);
+ }
+
+ # Check for graph file
+ $base = $absto;
+ $base =~ s/\.(gcda|da)$//;
+ if (! -e $base.".gcno" && ! -e $base.".bbg" && ! -e $base.".bb") {
+ die("ERROR: No graph file found for $absfrom in ".
+ dirname($base)."!\n");
+ }
+
+ symlink($absfrom, $absto) or
+ die("ERROR: could not create symlink at $absto: $!\n");
+}
+
+#
+# unlink_data_cb(datadir, rel, graphdir)
+#
+# Remove symbolic link from GRAPHDIR/REL to DATADIR/REL.
+#
+
+sub unlink_data_cb($$$)
+{
+ my ($datadir, $rel, $graphdir) = @_;
+ my $absfrom = catfile($datadir, $rel);
+ my $absto = catfile($graphdir, $rel);
+ my $target;
+
+ return if (!-l $absto);
+ $target = readlink($absto);
+ return if (!defined($target) || $target ne $absfrom);
+
+ unlink($absto) or
+ warn("WARNING: could not remove symlink $absto: $!\n");
+}
+
+#
+# link_data(datadir, graphdir, create)
+#
+# If CREATE is non-zero, create symbolic links in GRAPHDIR for data files
+# found in DATADIR. Otherwise remove link in GRAPHDIR.
+#
+
+sub link_data($$$)
+{
+ my ($datadir, $graphdir, $create) = @_;
+
+ $datadir = abs_path($datadir);
+ $graphdir = abs_path($graphdir);
+ if ($create) {
+ lcov_find($datadir, \&link_data_cb, $graphdir, '\.gcda$',
+ '\.da$');
+ } else {
+ lcov_find($datadir, \&unlink_data_cb, $graphdir, '\.gcda$',
+ '\.da$');
+ }
+}
+
+#
+# find_graph_cb(datadir, rel, count_ref)
+#
+# Count number of files found.
+#
+
+sub find_graph_cb($$$)
+{
+ my ($dir, $rel, $count_ref) = @_;
+
+ ($$count_ref)++;
+}
+
+#
+# find_graph(dir)
+#
+# Search DIR for a graph file. Return non-zero if one was found, zero otherwise.
+#
+
+sub find_graph($)
+{
+ my ($dir) = @_;
+ my $count = 0;
+
+ lcov_find($dir, \&find_graph_cb, \$count, '\.gcno$', '\.bb$', '\.bbg$');
+
+ return $count > 0 ? 1 : 0;
+}
+
+#
# package_capture()
#
# Capture coverage data from a package of unprocessed coverage data files
@@ -1242,7 +1427,16 @@ sub package_capture()
} else {
# Build directory needs to be passed to geninfo
$base_directory = $build;
- lcov_geninfo($dir);
+ if (find_graph($dir)) {
+ # Package contains graph files - collect from there
+ lcov_geninfo($dir);
+ } else {
+ # No graph files found, link data files next to
+ # graph files
+ link_data($dir, $base_directory, 1);
+ lcov_geninfo($base_directory);
+ link_data($dir, $base_directory, 0);
+ }
}
}
@@ -1397,10 +1591,12 @@ sub br_ivec_push($$$$)
my $i;
$vec = "" if (!defined($vec));
+ $block = $BR_VEC_MAX if $block < 0;
# Check if branch already exists in vector
for ($i = 0; $i < $num; $i++) {
my ($v_block, $v_branch, $v_taken) = br_ivec_get($vec, $i);
+ $v_block = $BR_VEC_MAX if $v_block < 0;
next if ($v_block != $block || $v_branch != $branch);
@@ -1437,6 +1633,7 @@ sub br_ivec_get($$)
# Retrieve data from vector
$block = vec($vec, $offset + $BR_BLOCK, $BR_VEC_WIDTH);
+ $block = -1 if ($block == $BR_VEC_MAX);
$branch = vec($vec, $offset + $BR_BRANCH, $BR_VEC_WIDTH);
$taken = vec($vec, $offset + $BR_TAKEN, $BR_VEC_WIDTH);
@@ -1492,6 +1689,10 @@ sub get_br_found_and_hit($)
# "func" -> \%funcdata
# "found" -> $lines_found (number of instrumented lines found in file)
# "hit" -> $lines_hit (number of executed lines in file)
+# "f_found" -> $fn_found (number of instrumented functions found in file)
+# "f_hit" -> $fn_hit (number of executed functions in file)
+# "b_found" -> $br_found (number of instrumented branches found in file)
+# "b_hit" -> $br_hit (number of executed branches in file)
# "check" -> \%checkdata
# "testfnc" -> \%testfncdata
# "sumfnc" -> \%sumfnccount
@@ -1576,14 +1777,14 @@ sub read_info_file($)
"compressed file $_[0]!\n");
# Open compressed file
- open(INFO_HANDLE, "gunzip -c $_[0]|")
+ open(INFO_HANDLE, "-|", "gunzip -c '$_[0]'")
or die("ERROR: cannot start gunzip to decompress ".
"file $_[0]!\n");
}
else
{
# Open decompressed file
- open(INFO_HANDLE, $_[0])
+ open(INFO_HANDLE, "<", $_[0])
or die("ERROR: cannot read file $_[0]!\n");
}
@@ -1674,6 +1875,8 @@ sub read_info_file($)
/^FN:(\d+),([^,]+)/ && do
{
+ last if (!$func_coverage);
+
# Function data found, add to structure
$funcdata->{$2} = $1;
@@ -1692,6 +1895,8 @@ sub read_info_file($)
/^FNDA:(\d+),([^,]+)/ && do
{
+ last if (!$func_coverage);
+
# Function call count found, add to structure
# Add summary counts
$sumfnccount->{$2} += $1;
@@ -1709,6 +1914,7 @@ sub read_info_file($)
my ($line, $block, $branch, $taken) =
($1, $2, $3, $4);
+ last if (!$br_coverage);
$sumbrcount->{$line} =
br_ivec_push($sumbrcount->{$line},
$block, $branch, $taken);
@@ -1912,8 +2118,8 @@ sub set_info_entry($$$$$$$$$;$$$$$$)
sub add_counts($$)
{
- my %data1 = %{$_[0]}; # Hash 1
- my %data2 = %{$_[1]}; # Hash 2
+ my $data1_ref = $_[0]; # Hash 1
+ my $data2_ref = $_[1]; # Hash 2
my %result; # Resulting hash
my $line; # Current line iteration scalar
my $data1_count; # Count of line in hash1
@@ -1921,10 +2127,10 @@ sub add_counts($$)
my $found = 0; # Total number of lines found
my $hit = 0; # Number of lines with a count > 0
- foreach $line (keys(%data1))
+ foreach $line (keys(%$data1_ref))
{
- $data1_count = $data1{$line};
- $data2_count = $data2{$line};
+ $data1_count = $data1_ref->{$line};
+ $data2_count = $data2_ref->{$line};
# Add counts if present in both hashes
if (defined($data2_count)) { $data1_count += $data2_count; }
@@ -1936,14 +2142,14 @@ sub add_counts($$)
if ($data1_count > 0) { $hit++; }
}
- # Add lines unique to data2
- foreach $line (keys(%data2))
+ # Add lines unique to data2_ref
+ foreach $line (keys(%$data2_ref))
{
- # Skip lines already in data1
- if (defined($data1{$line})) { next; }
+ # Skip lines already in data1_ref
+ if (defined($data1_ref->{$line})) { next; }
- # Copy count from data2
- $result{$line} = $data2{$line};
+ # Copy count from data2_ref
+ $result{$line} = $data2_ref->{$line};
$found++;
if ($result{$line} > 0) { $hit++; }
@@ -2439,7 +2645,7 @@ sub add_traces()
if ($to_file)
{
info("Writing data to $output_filename\n");
- open(INFO_HANDLE, ">$output_filename")
+ open(INFO_HANDLE, ">", $output_filename)
or die("ERROR: cannot write to $output_filename!\n");
@result = write_info_file(*INFO_HANDLE, $total_trace);
close(*INFO_HANDLE);
@@ -2548,6 +2754,7 @@ sub write_info_file(*$)
my ($block, $branch, $taken) =
br_ivec_get($brdata, $i);
+ $block = $BR_VEC_MAX if ($block < 0);
print(INFO_HANDLE "BRDA:$line,$block,".
"$branch,$taken\n");
$br_found++;
@@ -2589,7 +2796,7 @@ sub write_info_file(*$)
#
# transform_pattern(pattern)
#
-# Transform shell wildcard expression to equivalent PERL regular expression.
+# Transform shell wildcard expression to equivalent Perl regular expression.
# Return transformed pattern.
#
@@ -2669,7 +2876,7 @@ sub extract()
{
info("Extracted $extracted files\n");
info("Writing data to $output_filename\n");
- open(INFO_HANDLE, ">$output_filename")
+ open(INFO_HANDLE, ">", $output_filename)
or die("ERROR: cannot write to $output_filename!\n");
@result = write_info_file(*INFO_HANDLE, $data);
close(*INFO_HANDLE);
@@ -2725,7 +2932,7 @@ sub remove()
{
info("Deleted $removed files\n");
info("Writing data to $output_filename\n");
- open(INFO_HANDLE, ">$output_filename")
+ open(INFO_HANDLE, ">", $output_filename)
or die("ERROR: cannot write to $output_filename!\n");
@result = write_info_file(*INFO_HANDLE, $data);
close(*INFO_HANDLE);
@@ -2836,13 +3043,13 @@ sub shorten_number($$)
return '#';
}
-sub shorten_rate($$)
+sub shorten_rate($$$)
{
- my ($rate, $width) = @_;
- my $result = sprintf("%*.1f%%", $width - 3, $rate);
+ my ($hit, $found, $width) = @_;
+ my $result = rate($hit, $found, "%", 1, $width);
return $result if (length($result) <= $width);
- $result = sprintf("%*d%%", $width - 1, $rate);
+ $result = rate($hit, $found, "%", 0, $width);
return $result if (length($result) <= $width);
return "#";
}
@@ -3060,26 +3267,11 @@ sub list()
$br_total_hit += $br_hit;
# Determine line coverage rate for this file
- if ($found == 0) {
- $rate = "-";
- } else {
- $rate = shorten_rate(100 * $hit / $found,
- $fwidth[$F_LN_RATE]);
- }
+ $rate = shorten_rate($hit, $found, $fwidth[$F_LN_RATE]);
# Determine function coverage rate for this file
- if (!defined($fn_found) || $fn_found == 0) {
- $fnrate = "-";
- } else {
- $fnrate = shorten_rate(100 * $fn_hit / $fn_found,
- $fwidth[$F_FN_RATE]);
- }
+ $fnrate = shorten_rate($fn_hit, $fn_found, $fwidth[$F_FN_RATE]);
# Determine branch coverage rate for this file
- if (!defined($br_found) || $br_found == 0) {
- $brrate = "-";
- } else {
- $brrate = shorten_rate(100 * $br_hit / $br_found,
- $fwidth[$F_BR_RATE]);
- }
+ $brrate = shorten_rate($br_hit, $br_found, $fwidth[$F_BR_RATE]);
# Assemble line parameters
push(@file_data, $print_filename);
@@ -3095,26 +3287,13 @@ sub list()
}
# Determine total line coverage rate
- if ($total_found == 0) {
- $rate = "-";
- } else {
- $rate = shorten_rate(100 * $total_hit / $total_found,
- $fwidth[$F_LN_RATE]);
- }
+ $rate = shorten_rate($total_hit, $total_found, $fwidth[$F_LN_RATE]);
# Determine total function coverage rate
- if ($fn_total_found == 0) {
- $fnrate = "-";
- } else {
- $fnrate = shorten_rate(100 * $fn_total_hit / $fn_total_found,
- $fwidth[$F_FN_RATE]);
- }
+ $fnrate = shorten_rate($fn_total_hit, $fn_total_found,
+ $fwidth[$F_FN_RATE]);
# Determine total branch coverage rate
- if ($br_total_found == 0) {
- $brrate = "-";
- } else {
- $brrate = shorten_rate(100 * $br_total_hit / $br_total_found,
- $fwidth[$F_BR_RATE]);
- }
+ $brrate = shorten_rate($br_total_hit, $br_total_found,
+ $fwidth[$F_BR_RATE]);
# Print separator
print(("="x$barlen)."\n");
@@ -3253,14 +3432,14 @@ sub read_diff($)
"compressed file $diff_file!\n");
# Open compressed file
- open(HANDLE, "gunzip -c $diff_file|")
+ open(HANDLE, "-|", "gunzip -c '$diff_file'")
or die("ERROR: cannot start gunzip to decompress ".
"file $_[0]!\n");
}
else
{
# Open decompressed file
- open(HANDLE, $diff_file)
+ open(HANDLE, "<", $diff_file)
or die("ERROR: cannot read file $_[0]!\n");
}
@@ -3668,10 +3847,10 @@ sub adjust_fncdata($$$)
# Remove count data in testfncdata for functions which are no longer
# in funcdata
- foreach $testname (%{$testfncdata}) {
+ foreach $testname (keys(%{$testfncdata})) {
my $fnccount = $testfncdata->{$testname};
- foreach $func (%{$fnccount}) {
+ foreach $func (keys(%{$fnccount})) {
if (!defined($funcdata->{$func})) {
delete($fnccount->{$func});
}
@@ -3679,7 +3858,7 @@ sub adjust_fncdata($$$)
}
# Remove count data in sumfnccount for functions which are no longer
# in funcdata
- foreach $func (%{$sumfnccount}) {
+ foreach $func (keys(%{$sumfnccount})) {
if (!defined($funcdata->{$func})) {
delete($sumfnccount->{$func});
}
@@ -3886,7 +4065,7 @@ sub diff()
if ($to_file)
{
info("Writing data to $output_filename\n");
- open(INFO_HANDLE, ">$output_filename")
+ open(INFO_HANDLE, ">", $output_filename)
or die("ERROR: cannot write to $output_filename!\n");
@result = write_info_file(*INFO_HANDLE, $trace_data);
close(*INFO_HANDLE);
@@ -3899,6 +4078,59 @@ sub diff()
return @result;
}
+#
+# summary()
+#
+
+sub summary()
+{
+ my $filename;
+ my $current;
+ my $total;
+ my $ln_total_found;
+ my $ln_total_hit;
+ my $fn_total_found;
+ my $fn_total_hit;
+ my $br_total_found;
+ my $br_total_hit;
+
+ # Read and combine trace files
+ foreach $filename (@opt_summary) {
+ $current = read_info_file($filename);
+ if (!defined($total)) {
+ $total = $current;
+ } else {
+ $total = combine_info_files($total, $current);
+ }
+ }
+ # Calculate coverage data
+ foreach $filename (keys(%{$total}))
+ {
+ my $entry = $total->{$filename};
+ my $ln_found;
+ my $ln_hit;
+ my $fn_found;
+ my $fn_hit;
+ my $br_found;
+ my $br_hit;
+
+ (undef, undef, undef, undef, undef, undef, undef, undef,
+ $ln_found, $ln_hit, $fn_found, $fn_hit, $br_found,
+ $br_hit) = get_info_entry($entry);
+
+ # Add to totals
+ $ln_total_found += $ln_found;
+ $ln_total_hit += $ln_hit;
+ $fn_total_found += $fn_found;
+ $fn_total_hit += $fn_hit;
+ $br_total_found += $br_found;
+ $br_total_hit += $br_hit;
+ }
+
+
+ return ($ln_total_found, $ln_total_hit, $fn_total_found, $fn_total_hit,
+ $br_total_found, $br_total_hit);
+}
#
# system_no_output(mode, parameters)
@@ -3920,12 +4152,12 @@ sub system_no_output($@)
local *OLD_STDOUT;
# Save old stdout and stderr handles
- ($mode & 1) && open(OLD_STDOUT, ">>&STDOUT");
- ($mode & 2) && open(OLD_STDERR, ">>&STDERR");
+ ($mode & 1) && open(OLD_STDOUT, ">>&", "STDOUT");
+ ($mode & 2) && open(OLD_STDERR, ">>&", "STDERR");
# Redirect to /dev/null
- ($mode & 1) && open(STDOUT, ">/dev/null");
- ($mode & 2) && open(STDERR, ">/dev/null");
+ ($mode & 1) && open(STDOUT, ">", "/dev/null");
+ ($mode & 2) && open(STDERR, ">", "/dev/null");
system(@_);
$result = $?;
@@ -3935,8 +4167,8 @@ sub system_no_output($@)
($mode & 2) && close(STDERR);
# Restore old handles
- ($mode & 1) && open(STDOUT, ">>&OLD_STDOUT");
- ($mode & 2) && open(STDERR, ">>&OLD_STDERR");
+ ($mode & 1) && open(STDOUT, ">>&", "OLD_STDOUT");
+ ($mode & 2) && open(STDERR, ">>&", "OLD_STDERR");
return $result;
}
@@ -3957,7 +4189,7 @@ sub read_config($)
my $value;
local *HANDLE;
- if (!open(HANDLE, "<$filename"))
+ if (!open(HANDLE, "<", $filename))
{
warn("WARNING: cannot read configuration file $filename\n");
return undef;
@@ -3996,8 +4228,8 @@ sub read_config($)
# key_string => var_ref
#
# where KEY_STRING is a keyword and VAR_REF is a reference to an associated
-# variable. If the global configuration hash CONFIG contains a value for
-# keyword KEY_STRING, VAR_REF will be assigned the value for that keyword.
+# variable. If the global configuration hashes CONFIG or OPT_RC contain a value
+# for keyword KEY_STRING, VAR_REF will be assigned the value for that keyword.
#
sub apply_config($)
@@ -4006,8 +4238,9 @@ sub apply_config($)
foreach (keys(%{$ref}))
{
- if (defined($config->{$_}))
- {
+ if (defined($opt_rc{$_})) {
+ ${$ref->{$_}} = $opt_rc{$_};
+ } elsif (defined($config->{$_})) {
${$ref->{$_}} = $config->{$_};
}
}
@@ -4037,6 +4270,9 @@ sub abort_handler($)
sub temp_cleanup()
{
+ # Ensure temp directory is not in use by current process
+ chdir("/");
+
if (@temp_dirs) {
info("Removing temporary directories.\n");
foreach (@temp_dirs) {
@@ -4145,8 +4381,8 @@ sub get_overall_line($$$$)
return "no data found" if (!defined($found) || $found == 0);
$name = ($found == 1) ? $name_sn : $name_pl;
- return sprintf("%.1f%% (%d of %d %s)", $hit * 100 / $found, $hit,
- $found, $name);
+
+ return rate($hit, $found, "% ($hit of $found $name)");
}
@@ -4162,7 +4398,7 @@ sub print_overall_rate($$$$$$$$$)
my ($ln_do, $ln_found, $ln_hit, $fn_do, $fn_found, $fn_hit,
$br_do, $br_found, $br_hit) = @_;
- info("Overall coverage rate:\n");
+ info("Summary coverage rate:\n");
info(" lines......: %s\n",
get_overall_line($ln_found, $ln_hit, "line", "lines"))
if ($ln_do);
@@ -4173,3 +4409,38 @@ sub print_overall_rate($$$$$$$$$)
get_overall_line($br_found, $br_hit, "branch", "branches"))
if ($br_do);
}
+
+
+#
+# rate(hit, found[, suffix, precision, width])
+#
+# Return the coverage rate [0..100] for HIT and FOUND values. 0 is only
+# returned when HIT is 0. 100 is only returned when HIT equals FOUND.
+# PRECISION specifies the precision of the result. SUFFIX defines a
+# string that is appended to the result if FOUND is non-zero. Spaces
+# are added to the start of the resulting string until it is at least WIDTH
+# characters wide.
+#
+
+sub rate($$;$$$)
+{
+ my ($hit, $found, $suffix, $precision, $width) = @_;
+ my $rate;
+
+ # Assign defaults if necessary
+ $precision = 1 if (!defined($precision));
+ $suffix = "" if (!defined($suffix));
+ $width = 0 if (!defined($width));
+
+ return sprintf("%*s", $width, "-") if (!defined($found) || $found == 0);
+ $rate = sprintf("%.*f", $precision, $hit * 100 / $found);
+
+ # Adjust rates if necessary
+ if ($rate == 0 && $hit > 0) {
+ $rate = sprintf("%.*f", $precision, 1 / 10 ** $precision);
+ } elsif ($rate == 100 && $hit != $found) {
+ $rate = sprintf("%.*f", $precision, 100 - 1 / 10 ** $precision);
+ }
+
+ return sprintf("%*s", $width, $rate.$suffix);
+}