diff --git a/envcov.sh b/envcov.sh index 162cd16..f72a2c6 100644 --- a/envcov.sh +++ b/envcov.sh @@ -5,7 +5,7 @@ scripts="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" source "${scripts}/env.sh" -LCOV_PATH="${scripts}/lcov-1.11/bin" +LCOV_PATH="${scripts}/lcov-1.12/bin" OBJ_DIR="${OBJECT_FILE_DIR_normal}/${CURRENT_ARCH}" # Fix for the new LLVM-COV that requires gcov to have a -v parameter diff --git a/lcov-1.11/COPYING b/lcov-1.12/COPYING similarity index 100% rename from lcov-1.11/COPYING rename to lcov-1.12/COPYING diff --git a/lcov-1.11/bin/gendesc b/lcov-1.12/bin/gendesc old mode 100755 new mode 100644 similarity index 97% rename from lcov-1.11/bin/gendesc rename to lcov-1.12/bin/gendesc index 2a7ad56..a765124 --- a/lcov-1.11/bin/gendesc +++ b/lcov-1.12/bin/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.11'; +our $tool_dir = abs_path(dirname($0)); +our $lcov_version = 'LCOV version '.`$tool_dir/get_version.sh --full`; 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, diff --git a/lcov-1.11/bin/genhtml b/lcov-1.12/bin/genhtml old mode 100755 new mode 100644 similarity index 97% rename from lcov-1.11/bin/genhtml rename to lcov-1.12/bin/genhtml index 03ca57e..b89b0ef --- a/lcov-1.11/bin/genhtml +++ b/lcov-1.12/bin/genhtml @@ -69,14 +69,19 @@ 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.11'; +our $tool_dir = abs_path(dirname($0)); +our $lcov_version = 'LCOV version '.`$tool_dir/get_version.sh --full`; 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 @@ -189,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($); @@ -207,6 +212,7 @@ sub get_br_found_and_hit($); sub warn_handler($); sub die_handler($); sub parse_ignore_errors(@); +sub parse_dir_prefix(@); sub rate($$;$$$); @@ -254,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(); @@ -305,7 +312,6 @@ 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 # @@ -315,21 +321,25 @@ 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/; - -# Add current working directory if $tool_dir is not already an absolute path -if (! ($tool_dir =~ /^\/(.*)$/)) -{ - $tool_dir = "$cwd/$tool_dir"; -} - # 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($opt_config_file)) { $config = read_config($opt_config_file); @@ -363,6 +373,7 @@ if ($config || %opt_rc) "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, @@ -393,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, @@ -418,6 +429,7 @@ if (!GetOptions("output-directory|o=s" => \$output_directory, "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"); @@ -456,6 +468,9 @@ if ($version) # 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) { @@ -509,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); @@ -541,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) { @@ -608,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 @@ -736,10 +759,10 @@ sub rename_functions($$) # same demangled name. if (defined($newfuncdata{$cn}) && $newfuncdata{$cn} != $funcdata->{$fn}) { - die("ERROR: Demangled function name $fn ". - " maps to different lines (". + die("ERROR: Demangled function name $cn ". + "maps to different lines (". $newfuncdata{$cn}." vs ". - $funcdata->{$fn}.")\n"); + $funcdata->{$fn}.") in $filename\n"); } $newfuncdata{$cn} = $funcdata->{$fn}; } @@ -787,60 +810,6 @@ sub rename_functions($$) } } -# -# demangle_cpp(INFO) -# -# Demangle all function names found in INFO. -# -sub demangle_cpp($) -{ - my ($info) = @_; - my $fn_list = get_fn_list($info); - my @fn_list_demangled; - my $tmpfile; - my $handle; - my %demangled; - my $changed; - - # Nothing to do - return if (!@$fn_list); - - # Write list to temp file - (undef, $tmpfile) = tempfile(); - die("ERROR: could not create temporary file") if (!defined($tmpfile)); - open($handle, ">", $tmpfile) or - die("ERROR: could not write to $tmpfile: $!\n"); - print($handle join("\n", @$fn_list)); - close($handle); - - # Run c++ filt on tempfile file and parse output, creating a hash - open($handle, "-|", "c++filt < $tmpfile") or - die("ERROR: could not run c++filt: $!\n"); - @fn_list_demangled = <$handle>; - close($handle); - unlink($tmpfile) or - warn("WARNING: could not remove temporary file $tmpfile: $!\n"); - - if (scalar(@fn_list_demangled) != scalar(@$fn_list)) { - die("ERROR: c++filt output not as expected (". - scalar(@fn_list_demangled)." vs ". - scalar(@$fn_list).") lines\n"); - } - - # Build old_name -> new_name - $changed = 0; - for (my $i = 0; $i < scalar(@$fn_list); $i++) { - chomp($fn_list_demangled[$i]); - $demangled{$fn_list->[$i]} = $fn_list_demangled[$i]; - $changed++ if ($fn_list->[$i] ne $fn_list_demangled[$i]); - } - - info("Demangling $changed function names\n"); - - # Change all occurrences of function names in INFO - rename_functions($info, \%demangled); -} - # # gen_html() # @@ -898,9 +867,6 @@ sub gen_html() %info_data = %{apply_baseline(\%info_data, \%base_data)}; } - # Demangle C++ function names if requested - demangle_cpp(\%info_data) if ($demangle_cpp); - @dir_list = get_dir_list(keys(%info_data)); if ($no_prefix) @@ -908,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(1, keys(%info_data)); + 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 { @@ -925,11 +893,17 @@ sub gen_html() } else { - info("Using user-specified filename prefix \"". - "$dir_prefix\"\n"); - $dir_prefix =~ s/\/+$//; + 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) { @@ -975,10 +949,10 @@ sub gen_html() $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 @@ -1116,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; @@ -1299,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]; @@ -1471,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"); @@ -1537,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, @@ -5335,6 +5319,47 @@ sub funcview_get_sorted($$$) } 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; +} + # # write_function_table(filehandle, source_file, sumcount, funcdata, # sumfnccount, testfncdata, sumbrcount, testbrdata, @@ -5362,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); @@ -5376,7 +5402,12 @@ sub write_function_table(*$$$$$$$$$$) 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})) @@ -5389,6 +5420,9 @@ END_OF_HTML my $count = $sumfncdata->{$name}; my $countstyle; + # 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) { @@ -5683,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); + } } } @@ -5954,6 +5991,30 @@ sub parse_ignore_errors(@) } } +# +# 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]) # @@ -5971,7 +6032,7 @@ sub rate($$;$$$) my $rate; # Assign defaults if necessary - $precision = 1 if (!defined($precision)); + $precision = $default_precision if (!defined($precision)); $suffix = "" if (!defined($suffix)); $width = 0 if (!defined($width)); diff --git a/lcov-1.11/bin/geninfo b/lcov-1.12/bin/geninfo old mode 100755 new mode 100644 similarity index 98% rename from lcov-1.11/bin/geninfo rename to lcov-1.12/bin/geninfo index c518d80..c6d68e1 --- a/lcov-1.11/bin/geninfo +++ b/lcov-1.12/bin/geninfo @@ -55,13 +55,15 @@ use File::Spec::Functions qw /abs2rel catdir file_name_is_absolute splitdir 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.11'; +our $tool_dir = abs_path(dirname($0)); +our $lcov_version = 'LCOV version '.`$tool_dir/get_version.sh --full`; our $lcov_url = "http://ltp.sourceforge.net/coverage/lcov.php"; our $gcov_tool = "gcov"; our $tool_name = basename($0); @@ -86,12 +88,10 @@ our %ERROR_ID = ( 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"; -our $EXCL_BR_LINE = "LCOV_EXCL_BR_LINE"; # Compatibility mode values our $COMPAT_VALUE_OFF = 0; @@ -174,6 +174,7 @@ sub graph_error($$); sub graph_expect($); sub graph_read(*$;$$); sub graph_skip(*$;$); +sub uniq(@); sub sort_uniq(@); sub sort_uniq_lex(@); sub graph_cleanup($); @@ -255,6 +256,8 @@ 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); @@ -269,9 +272,6 @@ $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"; @@ -281,14 +281,17 @@ GetOptions("config-file=s" => \$opt_config_file, "rc=s%" => \%opt_rc); Getopt::Long::Configure("default"); -# Remove spaces around rc options -while (my ($key, $value) = each(%opt_rc)) { - delete($opt_rc{$key}); +{ + # Remove spaces around rc options + my %new_opt_rc; - $key =~ s/^\s+|\s+$//g; - $value =~ s/^\s+|\s+$//g; + while (my ($key, $value) = each(%opt_rc)) { + $key =~ s/^\s+|\s+$//g; + $value =~ s/^\s+|\s+$//g; - $opt_rc{$key} = $value; + $new_opt_rc{$key} = $value; + } + %opt_rc = %new_opt_rc; } # Read configuration file if available @@ -319,6 +322,8 @@ if ($config || %opt_rc) "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 @@ -348,6 +353,13 @@ if ($config || %opt_rc) $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 @@ -698,7 +710,7 @@ 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); if (!@file_list) { warn("WARNING: no $ext files found in $directory - ". @@ -1728,7 +1740,7 @@ 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; @@ -1741,7 +1753,7 @@ sub read_gcov_file($) } elsif (/$EXCL_BR_START/) { $exclude_br_flag = 1; } - if (/$EXCL_BR_LINE/ || $exclude_br_flag) { + if (/$excl_br_line/ || $exclude_br_flag) { $exclude_branch = 1; } else { $exclude_branch = 0; @@ -1827,7 +1839,7 @@ 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; @@ -1840,7 +1852,7 @@ sub read_gcov_file($) } elsif (/$EXCL_BR_START/) { $exclude_br_flag = 1; } - if (/$EXCL_BR_LINE/ || $exclude_br_flag) { + if (/$excl_br_line/ || $exclude_br_flag) { $exclude_branch = 1; } else { $exclude_branch = 0; @@ -1897,15 +1909,22 @@ sub get_gcov_version() local *HANDLE; my $version_string; my $result; + my $pipe_next_line; - open(GCOV_PIPE, "-|", "\"$gcov_tool\" --version") + open(GCOV_PIPE, "-|", "$gcov_tool --version") or die("ERROR: cannot retrieve gcov version!\n"); $version_string = ; # LLVM gcov keeps version information on the second line. # For example, gcov --version yields: # LLVM (http://llvm.org/): # LLVM version 3.4svn - $version_string = if ($version_string =~ /LLVM/); + + $pipe_next_line = ; + # 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: @@ -2126,7 +2145,7 @@ sub get_exclusion_data($) } elsif (/$EXCL_START/) { $flag = 1; } - if (/$EXCL_LINE/ || $flag) { + if (/$excl_line/ || $flag) { $list{$.} = 1; } } @@ -2285,6 +2304,13 @@ 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 (is_compat($COMPAT_MODE_HAMMER)) @@ -2528,6 +2554,27 @@ sub graph_skip(*$;$) return 0; } +# +# 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) # @@ -2714,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 diff --git a/lcov-1.11/bin/genpng b/lcov-1.12/bin/genpng old mode 100755 new mode 100644 similarity index 98% rename from lcov-1.11/bin/genpng rename to lcov-1.12/bin/genpng index 42cbde8..0ea4a06 --- a/lcov-1.11/bin/genpng +++ b/lcov-1.12/bin/genpng @@ -32,10 +32,12 @@ use strict; use File::Basename; use Getopt::Long; +use Cwd qw/abs_path/; # Constants -our $lcov_version = 'LCOV version 1.11'; +our $tool_dir = abs_path(dirname($0)); +our $lcov_version = 'LCOV version '.`$tool_dir/get_version.sh --full`; 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")) { diff --git a/lcov-1.11/bin/lcov b/lcov-1.12/bin/lcov old mode 100755 new mode 100644 similarity index 96% rename from lcov-1.11/bin/lcov rename to lcov-1.12/bin/lcov index f400532..abcf3d8 --- a/lcov-1.11/bin/lcov +++ b/lcov-1.12/bin/lcov @@ -71,7 +71,8 @@ use Cwd qw /abs_path getcwd/; # Global constants -our $lcov_version = 'LCOV version 1.11'; +our $tool_dir = abs_path(dirname($0)); +our $lcov_version = 'LCOV version '.`$tool_dir/get_version.sh --full`; our $lcov_url = "http://ltp.sourceforge.net/coverage/lcov.php"; our $tool_name = basename($0); @@ -180,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; @@ -214,29 +214,23 @@ $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/; - -# Add current working directory if $tool_dir is not already an absolute path -if (! ($tool_dir =~ /^\/(.*)$/)) -{ - $tool_dir = "$cwd/$tool_dir"; -} - # 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 -while (my ($key, $value) = each(%opt_rc)) { - delete($opt_rc{$key}); +{ + # Remove spaces around rc options + my %new_opt_rc; - $key =~ s/^\s+|\s+$//g; - $value =~ s/^\s+|\s+$//g; + while (my ($key, $value) = each(%opt_rc)) { + $key =~ s/^\s+|\s+$//g; + $value =~ s/^\s+|\s+$//g; - $opt_rc{$key} = $value; + $new_opt_rc{$key} = $value; + } + %opt_rc = %new_opt_rc; } # Read configuration file if available @@ -605,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) { @@ -936,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") or die("ERROR: could not process package $file\n"); + $count = 0; while () { 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"); @@ -1020,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"); @@ -1048,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"); @@ -1061,7 +1064,6 @@ sub create_package($$$;$) info(" data files ...........: $count\n"); } } - chdir($cwd); } sub find_link_fn($$$) @@ -1283,6 +1285,113 @@ sub kernel_capture() kernel_capture_from_dir($data_dir, $gcov_gkv, $build); } +# +# 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() # @@ -1318,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); + } } } @@ -4152,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) {