diff options
Diffstat (limited to '3rdParty/LCov/lcov')
-rwxr-xr-x | 3rdParty/LCov/lcov | 489 |
1 files changed, 380 insertions, 109 deletions
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); +} |