Index: third_party/lcov/bin/genhtml |
diff --git a/third_party/lcov/bin/genhtml b/third_party/lcov/bin/genhtml |
index af573ff7ed05a3cca0eb5edd606d47214bc48f91..8979f8c813d4b8418cfae6ff28a561d6fe6cab17 100755 |
--- a/third_party/lcov/bin/genhtml |
+++ b/third_party/lcov/bin/genhtml |
@@ -1,6 +1,6 @@ |
#!/usr/bin/perl -w |
# |
-# Copyright (c) International Business Machines Corp., 2002 |
+# 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 |
@@ -72,7 +72,7 @@ use Digest::MD5 qw(md5_base64); |
# Global constants |
our $title = "LCOV - code coverage report"; |
-our $lcov_version = "LCOV version 1.7"; |
+our $lcov_version = 'LCOV version 1.10'; |
our $lcov_url = "http://ltp.sourceforge.net/coverage/lcov.php"; |
our $tool_name = basename($0); |
@@ -81,13 +81,17 @@ our $tool_name = basename($0); |
# MED: $med_limit <= rate < $hi_limit graph color: orange |
# LO: 0 <= rate < $med_limit graph color: red |
-# For line coverage |
-our $hi_limit = 50; |
-our $med_limit = 15; |
+# For line coverage/all coverage types if not specified |
+our $hi_limit = 90; |
+our $med_limit = 75; |
# For function coverage |
-our $fn_hi_limit = 90; |
-our $fn_med_limit = 75; |
+our $fn_hi_limit; |
+our $fn_med_limit; |
+ |
+# For branch coverage |
+our $br_hi_limit; |
+our $br_med_limit; |
# Width of overview image |
our $overview_width = 80; |
@@ -107,7 +111,55 @@ our $nav_offset = 10; |
# specifies that offset in lines. |
our $func_offset = 2; |
-our $overview_title = "directory"; |
+our $overview_title = "top level"; |
+ |
+# Width for line coverage information in the source code view |
+our $line_field_width = 12; |
+ |
+# Width for branch coverage information in the source code view |
+our $br_field_width = 16; |
+ |
+# Internal Constants |
+ |
+# Header types |
+our $HDR_DIR = 0; |
+our $HDR_FILE = 1; |
+our $HDR_SOURCE = 2; |
+our $HDR_TESTDESC = 3; |
+our $HDR_FUNC = 4; |
+ |
+# Sort types |
+our $SORT_FILE = 0; |
+our $SORT_LINE = 1; |
+our $SORT_FUNC = 2; |
+our $SORT_BRANCH = 3; |
+ |
+# Fileview heading types |
+our $HEAD_NO_DETAIL = 1; |
+our $HEAD_DETAIL_HIDDEN = 2; |
+our $HEAD_DETAIL_SHOWN = 3; |
+ |
+# Offsets for storing branch coverage data in vectors |
+our $BR_BLOCK = 0; |
+our $BR_BRANCH = 1; |
+our $BR_TAKEN = 2; |
+our $BR_VEC_ENTRIES = 3; |
+our $BR_VEC_WIDTH = 32; |
+ |
+# Additional offsets used when converting branch coverage data to HTML |
+our $BR_LEN = 3; |
+our $BR_OPEN = 4; |
+our $BR_CLOSE = 5; |
+ |
+# Branch data combination types |
+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(*); |
@@ -118,21 +170,20 @@ sub process_file($$$); |
sub info(@); |
sub read_info_file($); |
sub get_info_entry($); |
-sub set_info_entry($$$$$$$;$$$$); |
-sub get_prefix(@); |
+sub set_info_entry($$$$$$$$$;$$$$$$); |
+sub get_prefix($@); |
sub shorten_prefix($); |
sub get_dir_list(@); |
sub get_relative_base_path($); |
sub read_testfile($); |
sub get_date_string(); |
-sub split_filename($); |
sub create_sub_dir($); |
sub subtract_counts($$); |
sub add_counts($$); |
sub apply_baseline($$); |
sub remove_unused_descriptions(); |
sub get_found_and_hit($); |
-sub get_affecting_tests($$); |
+sub get_affecting_tests($$$); |
sub combine_info_files($$); |
sub merge_checksums($$$); |
sub combine_info_entries($$$); |
@@ -142,6 +193,19 @@ sub read_config($); |
sub apply_config($); |
sub get_html_prolog($); |
sub get_html_epilog($); |
+sub write_dir_page($$$$$$$$$$$$$$$$$); |
+sub classify_rate($$$$); |
+sub br_taken_add($$); |
+sub br_taken_sub($$); |
+sub br_ivec_len($); |
+sub br_ivec_get($$); |
+sub br_ivec_push($$$$); |
+sub combine_brcount($$$); |
+sub get_br_found_and_hit($); |
+sub warn_handler($); |
+sub die_handler($); |
+sub parse_ignore_errors(@); |
+sub rate($$;$$$); |
# HTML related prototypes |
@@ -151,32 +215,31 @@ sub get_bar_graph_code($$$); |
sub write_png_files(); |
sub write_htaccess_file(); |
sub write_css_file(); |
-sub write_description_file($$$$$); |
-sub write_function_rable(*$$$); |
+sub write_description_file($$$$$$$); |
+sub write_function_table(*$$$$$$$$$$); |
sub write_html(*$); |
sub write_html_prolog(*$$); |
sub write_html_epilog(*$;$); |
-sub write_header(*$$$$$$$$); |
+sub write_header(*$$$$$$$$$$); |
sub write_header_prolog(*$); |
-sub write_header_line(*$@); |
+sub write_header_line(*@); |
sub write_header_epilog(*$); |
-sub write_file_table(*$$$$$$); |
-sub write_file_table_prolog(*$$$); |
-sub write_file_table_entry(*$$$$$$$); |
-sub write_file_table_detail_heading(*$$$); |
-sub write_file_table_detail_entry(*$$$$$); |
+sub write_file_table(*$$$$$$$); |
+sub write_file_table_prolog(*$@); |
+sub write_file_table_entry(*$$$@); |
+sub write_file_table_detail_entry(*$@); |
sub write_file_table_epilog(*); |
sub write_test_table_prolog(*$); |
sub write_test_table_entry(*$$); |
sub write_test_table_epilog(*); |
-sub write_source($$$$$$); |
+sub write_source($$$$$$$); |
sub write_source_prolog(*); |
-sub write_source_line(*$$$$$); |
+sub write_source_line(*$$$$$$); |
sub write_source_epilog(*); |
sub write_frameset(*$$$); |
@@ -204,8 +267,10 @@ 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; # 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 |
our $frames; # If set, use frames for source code view |
@@ -221,11 +286,19 @@ our $html_prolog; # Actual HTML prolog |
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"); |
+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 $cwd = `pwd`; # Current working directory |
chomp($cwd); |
@@ -239,14 +312,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"); |
+ |
# Read configuration file if available |
-if (-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"); |
} |
@@ -255,13 +339,14 @@ 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, |
"genhtml_med_limit" => \$med_limit, |
+ "genhtml_line_field_width" => \$line_field_width, |
"genhtml_overview_width" => \$overview_width, |
"genhtml_nav_resolution" => \$nav_resolution, |
"genhtml_nav_offset" => \$nav_offset, |
@@ -278,36 +363,57 @@ if ($config) |
"genhtml_function_hi_limit" => \$fn_hi_limit, |
"genhtml_function_med_limit" => \$fn_med_limit, |
"genhtml_function_coverage" => \$func_coverage, |
+ "genhtml_branch_hi_limit" => \$br_hi_limit, |
+ "genhtml_branch_med_limit" => \$br_med_limit, |
+ "genhtml_branch_coverage" => \$br_coverage, |
+ "genhtml_branch_field_width" => \$br_field_width, |
"genhtml_sort" => \$sort, |
+ "genhtml_charset" => \$charset, |
+ "lcov_function_coverage" => \$lcov_func_coverage, |
+ "lcov_branch_coverage" => \$lcov_branch_coverage, |
}); |
} |
+# 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=s" => \$output_directory, |
- "title=s" => \$test_title, |
- "description-file=s" => \$desc_filename, |
- "keep-descriptions" => \$keep_descriptions, |
- "css-file=s" => \$css_filename, |
- "baseline-file=s" => \$base_filename, |
- "prefix=s" => \$dir_prefix, |
+if (!GetOptions("output-directory|o=s" => \$output_directory, |
+ "title|t=s" => \$test_title, |
+ "description-file|d=s" => \$desc_filename, |
+ "keep-descriptions|k" => \$keep_descriptions, |
+ "css-file|c=s" => \$css_filename, |
+ "baseline-file|b=s" => \$base_filename, |
+ "prefix|p=s" => \$dir_prefix, |
"num-spaces=i" => \$tab_size, |
"no-prefix" => \$no_prefix, |
"no-sourceview" => \$no_sourceview, |
- "show-details" => \$show_details, |
- "frames" => \$frames, |
+ "show-details|s" => \$show_details, |
+ "frames|f" => \$frames, |
"highlight" => \$highlight, |
"legend" => \$legend, |
- "quiet" => \$quiet, |
+ "quiet|q" => \$quiet, |
"help|h|?" => \$help, |
- "version" => \$version, |
+ "version|v" => \$version, |
"html-prolog=s" => \$html_prolog_file, |
"html-epilog=s" => \$html_epilog_file, |
"html-extension=s" => \$html_ext, |
"html-gzip" => \$html_gzip, |
"function-coverage" => \$func_coverage, |
"no-function-coverage" => \$no_func_coverage, |
+ "branch-coverage" => \$br_coverage, |
+ "no-branch-coverage" => \$no_br_coverage, |
"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, |
)) |
{ |
print(STDERR "Use $tool_name --help to get usage information\n"); |
@@ -317,6 +423,9 @@ if (!GetOptions("output-directory=s" => \$output_directory, |
if ($no_func_coverage) { |
$func_coverage = 0; |
} |
+ if ($no_br_coverage) { |
+ $br_coverage = 0; |
+ } |
# Merge sort options |
if ($no_sort) { |
@@ -340,6 +449,9 @@ if ($version) |
exit(0); |
} |
+# Determine which errors the user wants us to ignore |
+parse_ignore_errors(@opt_ignore_errors); |
+ |
# Check for info filename |
if (!@info_filenames) |
{ |
@@ -400,16 +512,14 @@ if ($no_prefix && defined($dir_prefix)) |
$dir_prefix = undef; |
} |
+@fileview_sortlist = ($SORT_FILE); |
+@funcview_sortlist = ($SORT_FILE); |
+ |
if ($sort) { |
- @funcview_sortlist = (0, 1); |
- if ($func_coverage) { |
- @fileview_sortlist = (0, 1, 2); |
- } else { |
- @fileview_sortlist = (0, 1); |
- } |
-} else { |
- @fileview_sortlist = (0); |
- @funcview_sortlist = (0); |
+ push(@fileview_sortlist, $SORT_LINE); |
+ push(@fileview_sortlist, $SORT_FUNC) if ($func_coverage); |
+ push(@fileview_sortlist, $SORT_BRANCH) if ($br_coverage); |
+ push(@funcview_sortlist, $SORT_LINE); |
} |
if ($frames) |
@@ -418,6 +528,15 @@ if ($frames) |
do("$tool_dir/genpng"); |
} |
+# Ensure that the c++filt tool is available when using --demangle-cpp |
+if ($demangle_cpp) |
+{ |
+ if (system_no_output(3, "c++filt", "--version")) { |
+ die("ERROR: could not find c++filt tool needed for ". |
+ "--demangle-cpp\n"); |
+ } |
+} |
+ |
# Make sure output_directory exists, create it if necessary |
if ($output_directory) |
{ |
@@ -425,8 +544,7 @@ if ($output_directory) |
if (! -e _) |
{ |
- system("mkdir", "-p", $output_directory) |
- and die("ERROR: cannot create directory $_!\n"); |
+ create_sub_dir($output_directory); |
} |
} |
@@ -457,6 +575,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 |
@@ -467,6 +588,7 @@ Operation: |
-p, --prefix PREFIX Remove PREFIX from all directory names |
--no-prefix Do not remove prefix from directory names |
--(no-)function-coverage Enable (disable) function coverage display |
+ --(no-)branch-coverage Enable (disable) branch coverage display |
HTML output: |
-f, --frames Use HTML frames for source code view |
@@ -481,6 +603,7 @@ HTML output: |
--html-extension EXT Use EXT as filename extension for pages |
--html-gzip Use gzip to compress HTML |
--(no-)sort Enable (disable) sorted coverage views |
+ --demangle-cpp Demangle C++ function names |
For more information see: $lcov_url |
END_OF_USAGE |
@@ -508,6 +631,49 @@ sub get_rate($$) |
# |
+# get_overall_line(found, hit, name_singular, name_plural) |
+# |
+# Return a string containing overall information for the specified |
+# found/hit data. |
+# |
+ |
+sub get_overall_line($$$$) |
+{ |
+ my ($found, $hit, $name_sn, $name_pl) = @_; |
+ my $name; |
+ |
+ return "no data found" if (!defined($found) || $found == 0); |
+ $name = ($found == 1) ? $name_sn : $name_pl; |
+ return rate($hit, $found, "% ($hit of $found $name)"); |
+} |
+ |
+ |
+# |
+# print_overall_rate(ln_do, ln_found, ln_hit, fn_do, fn_found, fn_hit, br_do |
+# br_found, br_hit) |
+# |
+# Print overall coverage rates for the specified coverage types. |
+# |
+ |
+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(" lines......: %s\n", |
+ get_overall_line($ln_found, $ln_hit, "line", "lines")) |
+ if ($ln_do); |
+ info(" functions..: %s\n", |
+ get_overall_line($fn_found, $fn_hit, "function", "functions")) |
+ if ($fn_do); |
+ info(" branches...: %s\n", |
+ get_overall_line($br_found, $br_hit, "branch", "branches")) |
+ if ($br_do); |
+} |
+ |
+ |
+# |
# gen_html() |
# |
# Generate a set of HTML pages from contents of .info file INFO_FILENAME. |
@@ -527,10 +693,14 @@ sub gen_html() |
my $lines_hit; |
my $fn_found; |
my $fn_hit; |
+ my $br_found; |
+ my $br_hit; |
my $overall_found = 0; |
my $overall_hit = 0; |
my $total_fn_found = 0; |
my $total_fn_hit = 0; |
+ my $total_br_found = 0; |
+ my $total_br_hit = 0; |
my $dir_name; |
my $link_name; |
my @dir_list; |
@@ -570,7 +740,7 @@ sub gen_html() |
elsif (!defined($dir_prefix)) |
{ |
# Get prefix common to most directories in list |
- $dir_prefix = get_prefix(@dir_list); |
+ $dir_prefix = get_prefix(1, keys(%info_data)); |
if ($dir_prefix) |
{ |
@@ -625,9 +795,13 @@ sub gen_html() |
# Process each subdirectory and collect overview information |
foreach $dir_name (@dir_list) |
{ |
- ($lines_found, $lines_hit, $fn_found, $fn_hit) |
+ ($lines_found, $lines_hit, $fn_found, $fn_hit, |
+ $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) |
{ |
@@ -646,13 +820,16 @@ sub gen_html() |
} |
$overview{$dir_name} = [$lines_found, $lines_hit, $fn_found, |
- $fn_hit, $link_name, |
+ $fn_hit, $br_found, $br_hit, $link_name, |
get_rate($lines_found, $lines_hit), |
- get_rate($fn_found, $fn_hit)]; |
+ get_rate($fn_found, $fn_hit), |
+ get_rate($br_found, $br_hit)]; |
$overall_found += $lines_found; |
$overall_hit += $lines_hit; |
$total_fn_found += $fn_found; |
$total_fn_hit += $fn_hit; |
+ $total_br_found += $br_found; |
+ $total_br_hit += $br_hit; |
} |
# Generate overview page |
@@ -662,8 +839,8 @@ sub gen_html() |
foreach (@fileview_sortlist) { |
write_dir_page($fileview_sortname[$_], ".", "", $test_title, |
undef, $overall_found, $overall_hit, |
- $total_fn_found, $total_fn_hit, \%overview, |
- {}, {}, 0, $_); |
+ $total_fn_found, $total_fn_hit, $total_br_found, |
+ $total_br_hit, \%overview, {}, {}, {}, 0, $_); |
} |
# Check if there are any test case descriptions to write out |
@@ -672,37 +849,15 @@ sub gen_html() |
info("Writing test case description file.\n"); |
write_description_file( \%test_description, |
$overall_found, $overall_hit, |
- $total_fn_found, $total_fn_hit); |
- } |
- |
- chdir($cwd); |
- |
- info("Overall coverage rate:\n"); |
- |
- if ($overall_found == 0) |
- { |
- info(" lines......: no data found\n"); |
- return; |
+ $total_fn_found, $total_fn_hit, |
+ $total_br_found, $total_br_hit); |
} |
- info(" lines......: %.1f%% (%d of %d lines)\n", |
- $overall_hit * 100 / $overall_found, $overall_hit, |
- $overall_found,); |
- |
- if ($func_coverage) |
- { |
- if ($total_fn_found == 0) |
- { |
- info(" functions..: no data found\n"); |
- } |
- else |
- { |
- info(" functions..: %.1f%% (%d of %d functions)\n", |
- $total_fn_hit * 100 / $total_fn_found, |
- $total_fn_hit, $total_fn_found); |
- } |
- } |
+ print_overall_rate(1, $overall_found, $overall_hit, |
+ $func_coverage, $total_fn_found, $total_fn_hit, |
+ $br_coverage, $total_br_found, $total_br_hit); |
+ chdir($cwd); |
} |
# |
@@ -716,34 +871,36 @@ 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"); |
} |
} |
-sub write_dir_page($$$$$$$$$$$$$$) |
+sub write_dir_page($$$$$$$$$$$$$$$$$) |
{ |
my ($name, $rel_dir, $base_dir, $title, $trunc_dir, $overall_found, |
- $overall_hit, $total_fn_found, $total_fn_hit, $overview, |
- $testhash, $testfnchash, $view_type, $sort_type) = @_; |
+ $overall_hit, $total_fn_found, $total_fn_hit, $total_br_found, |
+ $total_br_hit, $overview, $testhash, $testfnchash, $testbrhash, |
+ $view_type, $sort_type) = @_; |
# Generate directory overview page including details |
html_create(*HTML_HANDLE, "$rel_dir/index$name.$html_ext"); |
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, |
- $total_fn_hit, $sort_type); |
+ $total_fn_hit, $total_br_found, $total_br_hit, $sort_type); |
write_file_table(*HTML_HANDLE, $base_dir, $overview, $testhash, |
- $testfnchash, $view_type, $sort_type); |
+ $testfnchash, $testbrhash, $view_type, $sort_type); |
write_html_epilog(*HTML_HANDLE, $base_dir); |
close(*HTML_HANDLE); |
} |
@@ -765,16 +922,22 @@ sub process_dir($) |
my $lines_hit; |
my $fn_found; |
my $fn_hit; |
+ my $br_found; |
+ my $br_hit; |
my $overall_found=0; |
my $overall_hit=0; |
my $total_fn_found=0; |
my $total_fn_hit=0; |
+ my $total_br_found = 0; |
+ my $total_br_hit = 0; |
my $base_name; |
my $extension; |
my $testdata; |
my %testhash; |
my $testfncdata; |
my %testfnchash; |
+ my $testbrdata; |
+ my %testbrhash; |
my @sort_list; |
local *HTML_HANDLE; |
@@ -793,6 +956,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); |
@@ -804,8 +971,9 @@ sub process_dir($) |
my $page_link; |
my $func_link; |
- ($lines_found, $lines_hit, $fn_found, $fn_hit, $testdata, |
- $testfncdata) = process_file($trunc_dir, $rel_dir, $filename); |
+ ($lines_found, $lines_hit, $fn_found, $fn_hit, $br_found, |
+ $br_hit, $testdata, $testfncdata, $testbrdata) = |
+ process_file($trunc_dir, $rel_dir, $filename); |
$base_name = basename($filename); |
@@ -819,18 +987,24 @@ sub process_dir($) |
$page_link = "$base_name.gcov.$html_ext"; |
} |
$overview{$base_name} = [$lines_found, $lines_hit, $fn_found, |
- $fn_hit, $page_link, |
+ $fn_hit, $br_found, $br_hit, |
+ $page_link, |
get_rate($lines_found, $lines_hit), |
- get_rate($fn_found, $fn_hit)]; |
+ get_rate($fn_found, $fn_hit), |
+ get_rate($br_found, $br_hit)]; |
$testhash{$base_name} = $testdata; |
$testfnchash{$base_name} = $testfncdata; |
+ $testbrhash{$base_name} = $testbrdata; |
$overall_found += $lines_found; |
$overall_hit += $lines_hit; |
$total_fn_found += $fn_found; |
$total_fn_hit += $fn_hit; |
+ |
+ $total_br_found += $br_found; |
+ $total_br_hit += $br_hit; |
} |
# Create sorted pages |
@@ -839,7 +1013,8 @@ sub process_dir($) |
write_dir_page($fileview_sortname[$_], $rel_dir, $base_dir, |
$test_title, $trunc_dir, $overall_found, |
$overall_hit, $total_fn_found, $total_fn_hit, |
- \%overview, {}, {}, 1, $_); |
+ $total_br_found, $total_br_hit, \%overview, {}, |
+ {}, {}, 1, $_); |
if (!$show_details) { |
next; |
} |
@@ -847,12 +1022,14 @@ sub process_dir($) |
write_dir_page("-detail".$fileview_sortname[$_], $rel_dir, |
$base_dir, $test_title, $trunc_dir, |
$overall_found, $overall_hit, $total_fn_found, |
- $total_fn_hit, \%overview, \%testhash, |
- \%testfnchash, 1, $_); |
+ $total_fn_hit, $total_br_found, $total_br_hit, |
+ \%overview, \%testhash, \%testfnchash, |
+ \%testbrhash, 1, $_); |
} |
# Calculate resulting line counts |
- return ($overall_found, $overall_hit, $total_fn_found, $total_fn_hit); |
+ return ($overall_found, $overall_hit, $total_fn_found, $total_fn_hit, |
+ $total_br_found, $total_br_hit); |
} |
@@ -913,11 +1090,12 @@ sub get_converted_lines($) |
} |
-sub write_function_page($$$$$$$$$$$$$$) |
+sub write_function_page($$$$$$$$$$$$$$$$$$) |
{ |
my ($base_dir, $rel_dir, $trunc_dir, $base_name, $title, |
- $lines_found, $lines_hit, $fn_found, $fn_hit, |
- $sumcount, $funcdata, $sumfnccount, $testfncdata, $sort_type) = @_; |
+ $lines_found, $lines_hit, $fn_found, $fn_hit, $br_found, $br_hit, |
+ $sumcount, $funcdata, $sumfnccount, $testfncdata, $sumbrcount, |
+ $testbrdata, $sort_type) = @_; |
my $pagetitle; |
my $filename; |
@@ -932,10 +1110,11 @@ sub write_function_page($$$$$$$$$$$$$$) |
write_html_prolog(*HTML_HANDLE, $base_dir, $pagetitle); |
write_header(*HTML_HANDLE, 4, "$trunc_dir/$base_name", |
"$rel_dir/$base_name", $lines_found, $lines_hit, |
- $fn_found, $fn_hit, $sort_type); |
+ $fn_found, $fn_hit, $br_found, $br_hit, $sort_type); |
write_function_table(*HTML_HANDLE, "$base_name.gcov.$html_ext", |
$sumcount, $funcdata, |
- $sumfnccount, $testfncdata, $base_name, |
+ $sumfnccount, $testfncdata, $sumbrcount, |
+ $testbrdata, $base_name, |
$base_dir, $sort_type); |
write_html_epilog(*HTML_HANDLE, $base_dir, 1); |
close(*HTML_HANDLE); |
@@ -962,25 +1141,31 @@ sub process_file($$$) |
my $checkdata; |
my $testfncdata; |
my $sumfnccount; |
+ my $testbrdata; |
+ my $sumbrcount; |
my $lines_found; |
my $lines_hit; |
my $fn_found; |
my $fn_hit; |
+ my $br_found; |
+ my $br_hit; |
my $converted; |
my @source; |
my $pagetitle; |
local *HTML_HANDLE; |
($testdata, $sumcount, $funcdata, $checkdata, $testfncdata, |
- $sumfnccount, $lines_found, $lines_hit, $fn_found, $fn_hit) |
+ $sumfnccount, $testbrdata, $sumbrcount, $lines_found, $lines_hit, |
+ $fn_found, $fn_hit, $br_found, $br_hit) |
= get_info_entry($info_data{$filename}); |
# Return after this point in case user asked us not to generate |
# source code view |
if ($no_sourceview) |
{ |
- return ($lines_found, $lines_hit, |
- $fn_found, $fn_hit, $testdata); |
+ return ($lines_found, $lines_hit, $fn_found, $fn_hit, |
+ $br_found, $br_hit, $testdata, $testfncdata, |
+ $testbrdata); |
} |
$converted = get_converted_lines($testdata); |
@@ -990,9 +1175,9 @@ sub process_file($$$) |
write_html_prolog(*HTML_HANDLE, $base_dir, $pagetitle); |
write_header(*HTML_HANDLE, 2, "$trunc_dir/$base_name", |
"$rel_dir/$base_name", $lines_found, $lines_hit, |
- $fn_found, $fn_hit, 0); |
+ $fn_found, $fn_hit, $br_found, $br_hit, 0); |
@source = write_source(*HTML_HANDLE, $filename, $sumcount, $checkdata, |
- $converted, $funcdata); |
+ $converted, $funcdata, $sumbrcount); |
write_html_epilog(*HTML_HANDLE, $base_dir, 1); |
close(*HTML_HANDLE); |
@@ -1003,17 +1188,20 @@ sub process_file($$$) |
write_function_page($base_dir, $rel_dir, $trunc_dir, |
$base_name, $test_title, |
$lines_found, $lines_hit, |
- $fn_found, $fn_hit, $sumcount, |
+ $fn_found, $fn_hit, $br_found, |
+ $br_hit, $sumcount, |
$funcdata, $sumfnccount, |
- $testfncdata, $_); |
+ $testfncdata, $sumbrcount, |
+ $testbrdata, $_); |
} |
} |
# Additional files are needed in case of frame output |
if (!$frames) |
{ |
- return ($lines_found, $lines_hit, |
- $fn_found, $fn_hit, $testdata); |
+ return ($lines_found, $lines_hit, $fn_found, $fn_hit, |
+ $br_found, $br_hit, $testdata, $testfncdata, |
+ $testbrdata); |
} |
# Create overview png file |
@@ -1033,8 +1221,8 @@ sub process_file($$$) |
scalar(@source)); |
close(*HTML_HANDLE); |
- return ($lines_found, $lines_hit, $fn_found, $fn_hit, $testdata, |
- $testfncdata); |
+ return ($lines_found, $lines_hit, $fn_found, $fn_hit, $br_found, |
+ $br_hit, $testdata, $testfncdata, $testbrdata); |
} |
@@ -1054,16 +1242,22 @@ sub process_file($$$) |
# "check" -> \%checkdata |
# "testfnc" -> \%testfncdata |
# "sumfnc" -> \%sumfnccount |
+# "testbr" -> \%testbrdata |
+# "sumbr" -> \%sumbrcount |
# |
# %testdata : name of test affecting this file -> \%testcount |
# %testfncdata: name of test affecting this file -> \%testfnccount |
+# %testbrdata: name of test affecting this file -> \%testbrcount |
# |
# %testcount : line number -> execution count for a single test |
# %testfnccount: function name -> execution count for a single test |
+# %testbrcount : line number -> branch coverage data for a single test |
# %sumcount : line number -> execution count for all tests |
# %sumfnccount : function name -> execution count for all tests |
+# %sumbrcount : line number -> branch coverage data for all tests |
# %funcdata : function name -> line number |
# %checkdata : line number -> checksum of source code line |
+# $brdata : vector of items: block, branch, taken |
# |
# Note that .info file sections referring to the same file and test name |
# will automatically be combined by adding all execution counts. |
@@ -1088,6 +1282,9 @@ sub read_info_file($) |
my $testfncdata; |
my $testfnccount; |
my $sumfnccount; |
+ my $testbrdata; |
+ my $testbrcount; |
+ my $sumbrcount; |
my $line; # Current line read from .info file |
my $testname; # Current test name |
my $filename; # Current filename |
@@ -1096,6 +1293,8 @@ sub read_info_file($) |
my $negative; # If set, warn about negative counts |
my $changed_testname; # If set, warn about changed testname |
my $line_checksum; # Checksum of current line |
+ my $br_found; |
+ my $br_hit; |
local *INFO_HANDLE; # Filehandle for .info file |
info("Reading data file $tracefile\n"); |
@@ -1126,14 +1325,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"); |
} |
@@ -1146,7 +1345,7 @@ sub read_info_file($) |
# Switch statement |
foreach ($line) |
{ |
- /^TN:([^,]*)/ && do |
+ /^TN:([^,]*)(,diff)?/ && do |
{ |
# Test name information found |
$testname = defined($1) ? $1 : ""; |
@@ -1154,6 +1353,7 @@ sub read_info_file($) |
{ |
$changed_testname = 1; |
} |
+ $testname .= $2 if (defined($2)); |
last; |
}; |
@@ -1165,18 +1365,21 @@ sub read_info_file($) |
$data = $result{$filename}; |
($testdata, $sumcount, $funcdata, $checkdata, |
- $testfncdata, $sumfnccount) = |
+ $testfncdata, $sumfnccount, $testbrdata, |
+ $sumbrcount) = |
get_info_entry($data); |
if (defined($testname)) |
{ |
$testcount = $testdata->{$testname}; |
$testfnccount = $testfncdata->{$testname}; |
+ $testbrcount = $testbrdata->{$testname}; |
} |
else |
{ |
$testcount = {}; |
$testfnccount = {}; |
+ $testbrcount = {}; |
} |
last; |
}; |
@@ -1220,6 +1423,8 @@ sub read_info_file($) |
/^FN:(\d+),([^,]+)/ && do |
{ |
+ last if (!$func_coverage); |
+ |
# Function data found, add to structure |
$funcdata->{$2} = $1; |
@@ -1238,6 +1443,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; |
@@ -1249,6 +1455,28 @@ sub read_info_file($) |
} |
last; |
}; |
+ |
+ /^BRDA:(\d+),(\d+),(\d+),(\d+|-)/ && do { |
+ # Branch coverage data found |
+ my ($line, $block, $branch, $taken) = |
+ ($1, $2, $3, $4); |
+ |
+ last if (!$br_coverage); |
+ $sumbrcount->{$line} = |
+ br_ivec_push($sumbrcount->{$line}, |
+ $block, $branch, $taken); |
+ |
+ # Add test-specific counts |
+ if (defined($testname)) { |
+ $testbrcount->{$line} = |
+ br_ivec_push( |
+ $testbrcount->{$line}, |
+ $block, $branch, |
+ $taken); |
+ } |
+ last; |
+ }; |
+ |
/^end_of_record/ && do |
{ |
# Found end of section marker |
@@ -1261,12 +1489,16 @@ sub read_info_file($) |
$testcount; |
$testfncdata->{$testname} = |
$testfnccount; |
+ $testbrdata->{$testname} = |
+ $testbrcount; |
} |
set_info_entry($data, $testdata, |
$sumcount, $funcdata, |
$checkdata, $testfncdata, |
- $sumfnccount); |
+ $sumfnccount, |
+ $testbrdata, |
+ $sumbrcount); |
$result{$filename} = $data; |
last; |
} |
@@ -1284,7 +1516,8 @@ sub read_info_file($) |
$data = $result{$filename}; |
($testdata, $sumcount, undef, undef, $testfncdata, |
- $sumfnccount) = get_info_entry($data); |
+ $sumfnccount, $testbrdata, $sumbrcount) = |
+ get_info_entry($data); |
# Filter out empty files |
if (scalar(keys(%{$sumcount})) == 0) |
@@ -1323,6 +1556,12 @@ sub read_info_file($) |
} |
} |
$data->{"f_hit"} = $hitcount; |
+ |
+ # Get found/hit values for branch data |
+ ($br_found, $br_hit) = get_br_found_and_hit($sumbrcount); |
+ |
+ $data->{"b_found"} = $br_found; |
+ $data->{"b_hit"} = $br_hit; |
} |
if (scalar(keys(%result)) == 0) |
@@ -1362,26 +1601,32 @@ sub get_info_entry($) |
my $checkdata_ref = $_[0]->{"check"}; |
my $testfncdata = $_[0]->{"testfnc"}; |
my $sumfnccount = $_[0]->{"sumfnc"}; |
+ my $testbrdata = $_[0]->{"testbr"}; |
+ my $sumbrcount = $_[0]->{"sumbr"}; |
my $lines_found = $_[0]->{"found"}; |
my $lines_hit = $_[0]->{"hit"}; |
my $fn_found = $_[0]->{"f_found"}; |
my $fn_hit = $_[0]->{"f_hit"}; |
+ my $br_found = $_[0]->{"b_found"}; |
+ my $br_hit = $_[0]->{"b_hit"}; |
return ($testdata_ref, $sumcount_ref, $funcdata_ref, $checkdata_ref, |
- $testfncdata, $sumfnccount, $lines_found, $lines_hit, |
- $fn_found, $fn_hit); |
+ $testfncdata, $sumfnccount, $testbrdata, $sumbrcount, |
+ $lines_found, $lines_hit, $fn_found, $fn_hit, |
+ $br_found, $br_hit); |
} |
# |
# set_info_entry(hash_ref, testdata_ref, sumcount_ref, funcdata_ref, |
-# checkdata_ref, testfncdata_ref, sumfcncount_ref[,lines_found, |
-# lines_hit, f_found, f_hit]) |
+# checkdata_ref, testfncdata_ref, sumfcncount_ref, |
+# testbrdata_ref, sumbrcount_ref[,lines_found, |
+# lines_hit, f_found, f_hit, $b_found, $b_hit]) |
# |
# Update the hash referenced by HASH_REF with the provided data references. |
# |
-sub set_info_entry($$$$$$$;$$$$) |
+sub set_info_entry($$$$$$$$$;$$$$$$) |
{ |
my $data_ref = $_[0]; |
@@ -1391,11 +1636,15 @@ sub set_info_entry($$$$$$$;$$$$) |
$data_ref->{"check"} = $_[4]; |
$data_ref->{"testfnc"} = $_[5]; |
$data_ref->{"sumfnc"} = $_[6]; |
- |
- if (defined($_[7])) { $data_ref->{"found"} = $_[7]; } |
- if (defined($_[8])) { $data_ref->{"hit"} = $_[8]; } |
- if (defined($_[9])) { $data_ref->{"f_found"} = $_[9]; } |
- if (defined($_[10])) { $data_ref->{"f_hit"} = $_[10]; } |
+ $data_ref->{"testbr"} = $_[7]; |
+ $data_ref->{"sumbr"} = $_[8]; |
+ |
+ if (defined($_[9])) { $data_ref->{"found"} = $_[9]; } |
+ if (defined($_[10])) { $data_ref->{"hit"} = $_[10]; } |
+ if (defined($_[11])) { $data_ref->{"f_found"} = $_[11]; } |
+ if (defined($_[12])) { $data_ref->{"f_hit"} = $_[12]; } |
+ if (defined($_[13])) { $data_ref->{"b_found"} = $_[13]; } |
+ if (defined($_[14])) { $data_ref->{"b_hit"} = $_[14]; } |
} |
@@ -1503,7 +1752,9 @@ sub merge_func_data($$$) |
my %result; |
my $func; |
- %result = %{$funcdata1}; |
+ if (defined($funcdata1)) { |
+ %result = %{$funcdata1}; |
+ } |
foreach $func (keys(%{$funcdata2})) { |
my $line1 = $result{$func}; |
@@ -1535,7 +1786,9 @@ sub add_fnccount($$) |
my $fn_hit; |
my $function; |
- %result = %{$fnccount1}; |
+ if (defined($fnccount1)) { |
+ %result = %{$fnccount1}; |
+ } |
foreach $function (keys(%{$fnccount2})) { |
$result{$function} += $fnccount2->{$function}; |
} |
@@ -1589,6 +1842,167 @@ sub add_testfncdata($$) |
return \%result; |
} |
+ |
+# |
+# brcount_to_db(brcount) |
+# |
+# Convert brcount data to the following format: |
+# |
+# db: line number -> block hash |
+# block hash: block number -> branch hash |
+# branch hash: branch number -> taken value |
+# |
+ |
+sub brcount_to_db($) |
+{ |
+ my ($brcount) = @_; |
+ my $line; |
+ my $db; |
+ |
+ # Add branches from first count to database |
+ foreach $line (keys(%{$brcount})) { |
+ my $brdata = $brcount->{$line}; |
+ my $i; |
+ my $num = br_ivec_len($brdata); |
+ |
+ for ($i = 0; $i < $num; $i++) { |
+ my ($block, $branch, $taken) = br_ivec_get($brdata, $i); |
+ |
+ $db->{$line}->{$block}->{$branch} = $taken; |
+ } |
+ } |
+ |
+ return $db; |
+} |
+ |
+ |
+# |
+# db_to_brcount(db) |
+# |
+# Convert branch coverage data back to brcount format. |
+# |
+ |
+sub db_to_brcount($) |
+{ |
+ my ($db) = @_; |
+ my $line; |
+ my $brcount = {}; |
+ my $br_found = 0; |
+ my $br_hit = 0; |
+ |
+ # Convert database back to brcount format |
+ foreach $line (sort({$a <=> $b} keys(%{$db}))) { |
+ my $ldata = $db->{$line}; |
+ my $brdata; |
+ my $block; |
+ |
+ foreach $block (sort({$a <=> $b} keys(%{$ldata}))) { |
+ my $bdata = $ldata->{$block}; |
+ my $branch; |
+ |
+ foreach $branch (sort({$a <=> $b} keys(%{$bdata}))) { |
+ my $taken = $bdata->{$branch}; |
+ |
+ $br_found++; |
+ $br_hit++ if ($taken ne "-" && $taken > 0); |
+ $brdata = br_ivec_push($brdata, $block, |
+ $branch, $taken); |
+ } |
+ } |
+ $brcount->{$line} = $brdata; |
+ } |
+ |
+ return ($brcount, $br_found, $br_hit); |
+} |
+ |
+ |
+# |
+# combine_brcount(brcount1, brcount2, type) |
+# |
+# If add is BR_ADD, add branch coverage data and return list (brcount_added, |
+# br_found, br_hit). If add is BR_SUB, subtract the taken values of brcount2 |
+# from brcount1 and return (brcount_sub, br_found, br_hit). |
+# |
+ |
+sub combine_brcount($$$) |
+{ |
+ my ($brcount1, $brcount2, $type) = @_; |
+ my $line; |
+ my $block; |
+ my $branch; |
+ my $taken; |
+ my $db; |
+ my $br_found = 0; |
+ my $br_hit = 0; |
+ my $result; |
+ |
+ # Convert branches from first count to database |
+ $db = brcount_to_db($brcount1); |
+ # Combine values from database and second count |
+ foreach $line (keys(%{$brcount2})) { |
+ my $brdata = $brcount2->{$line}; |
+ my $num = br_ivec_len($brdata); |
+ my $i; |
+ |
+ for ($i = 0; $i < $num; $i++) { |
+ ($block, $branch, $taken) = br_ivec_get($brdata, $i); |
+ my $new_taken = $db->{$line}->{$block}->{$branch}; |
+ |
+ if ($type == $BR_ADD) { |
+ $new_taken = br_taken_add($new_taken, $taken); |
+ } elsif ($type == $BR_SUB) { |
+ $new_taken = br_taken_sub($new_taken, $taken); |
+ } |
+ $db->{$line}->{$block}->{$branch} = $new_taken |
+ if (defined($new_taken)); |
+ } |
+ } |
+ # Convert database back to brcount format |
+ ($result, $br_found, $br_hit) = db_to_brcount($db); |
+ |
+ return ($result, $br_found, $br_hit); |
+} |
+ |
+ |
+# |
+# add_testbrdata(testbrdata1, testbrdata2) |
+# |
+# Add branch coverage data for several tests. Return reference to |
+# added_testbrdata. |
+# |
+ |
+sub add_testbrdata($$) |
+{ |
+ my ($testbrdata1, $testbrdata2) = @_; |
+ my %result; |
+ my $testname; |
+ |
+ foreach $testname (keys(%{$testbrdata1})) { |
+ if (defined($testbrdata2->{$testname})) { |
+ my $brcount; |
+ |
+ # Branch coverage data for this testname exists |
+ # in both data sets: add |
+ ($brcount) = combine_brcount($testbrdata1->{$testname}, |
+ $testbrdata2->{$testname}, $BR_ADD); |
+ $result{$testname} = $brcount; |
+ next; |
+ } |
+ # Branch coverage data for this testname is unique to |
+ # data set 1: copy |
+ $result{$testname} = $testbrdata1->{$testname}; |
+ } |
+ |
+ # Add count data for testnames unique to data set 2 |
+ foreach $testname (keys(%{$testbrdata2})) { |
+ if (!defined($result{$testname})) { |
+ $result{$testname} = $testbrdata2->{$testname}; |
+ } |
+ } |
+ return \%result; |
+} |
+ |
+ |
# |
# combine_info_entries(entry_ref1, entry_ref2, filename) |
# |
@@ -1605,6 +2019,8 @@ sub combine_info_entries($$$) |
my $checkdata1; |
my $testfncdata1; |
my $sumfnccount1; |
+ my $testbrdata1; |
+ my $sumbrcount1; |
my $entry2 = $_[1]; # Reference to hash containing second entry |
my $testdata2; |
@@ -1613,6 +2029,8 @@ sub combine_info_entries($$$) |
my $checkdata2; |
my $testfncdata2; |
my $sumfnccount2; |
+ my $testbrdata2; |
+ my $sumbrcount2; |
my %result; # Hash containing combined entry |
my %result_testdata; |
@@ -1620,19 +2038,23 @@ sub combine_info_entries($$$) |
my $result_funcdata; |
my $result_testfncdata; |
my $result_sumfnccount; |
+ my $result_testbrdata; |
+ my $result_sumbrcount; |
my $lines_found; |
my $lines_hit; |
my $fn_found; |
my $fn_hit; |
+ my $br_found; |
+ my $br_hit; |
my $testname; |
my $filename = $_[2]; |
# Retrieve data |
($testdata1, $sumcount1, $funcdata1, $checkdata1, $testfncdata1, |
- $sumfnccount1) = get_info_entry($entry1); |
+ $sumfnccount1, $testbrdata1, $sumbrcount1) = get_info_entry($entry1); |
($testdata2, $sumcount2, $funcdata2, $checkdata2, $testfncdata2, |
- $sumfnccount2) = get_info_entry($entry2); |
+ $sumfnccount2, $testbrdata2, $sumbrcount2) = get_info_entry($entry2); |
# Merge checksums |
$checkdata1 = merge_checksums($checkdata1, $checkdata2, $filename); |
@@ -1645,6 +2067,11 @@ sub combine_info_entries($$$) |
($result_sumfnccount, $fn_found, $fn_hit) = |
add_fnccount($sumfnccount1, $sumfnccount2); |
+ # Combine branch coverage data |
+ $result_testbrdata = add_testbrdata($testbrdata1, $testbrdata2); |
+ ($result_sumbrcount, $br_found, $br_hit) = |
+ combine_brcount($sumbrcount1, $sumbrcount2, $BR_ADD); |
+ |
# Combine testdata |
foreach $testname (keys(%{$testdata1})) |
{ |
@@ -1687,8 +2114,9 @@ sub combine_info_entries($$$) |
# Store result |
set_info_entry(\%result, \%result_testdata, $result_sumcount, |
$result_funcdata, $checkdata1, $result_testfncdata, |
- $result_sumfnccount, $lines_found, $lines_hit, |
- $fn_found, $fn_hit); |
+ $result_sumfnccount, $result_testbrdata, |
+ $result_sumbrcount, $lines_found, $lines_hit, |
+ $fn_found, $fn_hit, $br_found, $br_hit); |
return(\%result); |
} |
@@ -1730,16 +2158,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 |
@@ -1748,12 +2177,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"; |
@@ -1761,6 +2192,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)) |
{ |
@@ -1789,6 +2234,8 @@ sub get_prefix(@) |
} |
} |
+ $current =~ s/\/$//; |
+ |
return($current); |
} |
@@ -1880,7 +2327,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>) |
@@ -1986,14 +2433,17 @@ sub get_date_string() |
sub create_sub_dir($) |
{ |
- system("mkdir", "-p" ,$_[0]) |
- and die("ERROR: cannot create directory $_!\n"); |
+ my ($dir) = @_; |
+ |
+ system("mkdir", "-p" ,$dir) |
+ and die("ERROR: cannot create directory $dir!\n"); |
} |
# |
# write_description_file(descriptions, overall_found, overall_hit, |
-# total_fn_found, total_fn_hit) |
+# total_fn_found, total_fn_hit, total_br_found, |
+# total_br_hit) |
# |
# Write HTML file containing all test case descriptions. DESCRIPTIONS is a |
# reference to a hash containing a mapping |
@@ -2003,20 +2453,22 @@ sub create_sub_dir($) |
# Die on error. |
# |
-sub write_description_file($$$$$) |
+sub write_description_file($$$$$$$) |
{ |
my %description = %{$_[0]}; |
my $found = $_[1]; |
my $hit = $_[2]; |
my $fn_found = $_[3]; |
my $fn_hit = $_[4]; |
+ my $br_found = $_[5]; |
+ my $br_hit = $_[6]; |
my $test_name; |
local *HTML_HANDLE; |
html_create(*HTML_HANDLE,"descriptions.$html_ext"); |
write_html_prolog(*HTML_HANDLE, "", "LCOV - test case descriptions"); |
write_header(*HTML_HANDLE, 3, "", "", $found, $hit, $fn_found, |
- $fn_hit, 0); |
+ $fn_hit, $br_found, $br_hit, 0); |
write_test_table_prolog(*HTML_HANDLE, |
"Test case descriptions - alphabetical list"); |
@@ -2146,7 +2598,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{$_}})); |
@@ -2164,7 +2616,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") |
@@ -2197,7 +2649,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"); |
@@ -2328,17 +2780,6 @@ sub write_css_file() |
background-color: #FF0000; |
} |
- /* All views: header legend item for legend entry */ |
- td.headerItemLeg |
- { |
- text-align: right; |
- padding-right: 6px; |
- font-family: sans-serif; |
- font-weight: bold; |
- vertical-align: bottom; |
- white-space: nowrap; |
- } |
- |
/* All views: header legend value for legend entry */ |
td.headerValueLeg |
{ |
@@ -2419,6 +2860,7 @@ sub write_css_file() |
padding-right: 10px; |
background-color: #A7FC9D; |
font-weight: bold; |
+ font-family: sans-serif; |
} |
/* Directory view/File view (all): line count entry for files with |
@@ -2430,16 +2872,7 @@ sub write_css_file() |
padding-right: 10px; |
background-color: #A7FC9D; |
white-space: nowrap; |
- } |
- |
- /* Directory view/File view (all): legend entry for high coverage |
- rate */ |
- span.coverLegendHi |
- { |
- padding-left: 10px; |
- padding-right: 10px; |
- padding-bottom: 2px; |
- background-color: #A7FC9D; |
+ font-family: sans-serif; |
} |
/* Directory view/File view (all): percentage entry for files with |
@@ -2451,6 +2884,7 @@ sub write_css_file() |
padding-right: 10px; |
background-color: #FFEA20; |
font-weight: bold; |
+ font-family: sans-serif; |
} |
/* Directory view/File view (all): line count entry for files with |
@@ -2462,16 +2896,7 @@ sub write_css_file() |
padding-right: 10px; |
background-color: #FFEA20; |
white-space: nowrap; |
- } |
- |
- /* Directory view/File view (all): legend entry for medium coverage |
- rate */ |
- span.coverLegendMed |
- { |
- padding-left: 10px; |
- padding-right: 10px; |
- padding-bottom: 2px; |
- background-color: #FFEA20; |
+ font-family: sans-serif; |
} |
/* Directory view/File view (all): percentage entry for files with |
@@ -2483,6 +2908,7 @@ sub write_css_file() |
padding-right: 10px; |
background-color: #FF0000; |
font-weight: bold; |
+ font-family: sans-serif; |
} |
/* Directory view/File view (all): line count entry for files with |
@@ -2494,53 +2920,28 @@ sub write_css_file() |
padding-right: 10px; |
background-color: #FF0000; |
white-space: nowrap; |
- } |
- |
- /* Directory view/File view (all): legend entry for low coverage |
- rate */ |
- span.coverLegendLo |
- { |
- padding-left: 10px; |
- padding-right: 10px; |
- padding-bottom: 2px; |
- background-color: #FF0000; |
+ font-family: sans-serif; |
} |
/* File view (all): "show/hide details" link format */ |
a.detail:link |
{ |
color: #B8D0FF; |
+ font-size:80%; |
} |
/* File view (all): "show/hide details" link - visited format */ |
a.detail:visited |
{ |
color: #B8D0FF; |
+ font-size:80%; |
} |
/* File view (all): "show/hide details" link - activated format */ |
a.detail:active |
{ |
color: #FFFFFF; |
- } |
- |
- /* File view (detail): test name table headline format */ |
- td.testNameHead |
- { |
- text-align: right; |
- padding-right: 10px; |
- background-color: #DAE7FE; |
- font-family: sans-serif; |
- font-weight: bold; |
- } |
- |
- /* File view (detail): test lines table headline format */ |
- td.testLinesHead |
- { |
- text-align: center; |
- background-color: #DAE7FE; |
- font-family: sans-serif; |
- font-weight: bold; |
+ font-size:80%; |
} |
/* File view (detail): test name entry */ |
@@ -2549,6 +2950,7 @@ sub write_css_file() |
text-align: right; |
padding-right: 10px; |
background-color: #DAE7FE; |
+ font-family: sans-serif; |
} |
/* File view (detail): test percentage entry */ |
@@ -2558,6 +2960,7 @@ sub write_css_file() |
padding-left: 10px; |
padding-right: 10px; |
background-color: #DAE7FE; |
+ font-family: sans-serif; |
} |
/* File view (detail): test lines count entry */ |
@@ -2567,6 +2970,7 @@ sub write_css_file() |
padding-left: 10px; |
padding-right: 10px; |
background-color: #DAE7FE; |
+ font-family: sans-serif; |
} |
/* Test case descriptions: test name format*/ |
@@ -2605,6 +3009,7 @@ sub write_css_file() |
padding-right: 10px; |
background-color: #FF0000; |
font-weight: bold; |
+ font-family: sans-serif; |
} |
/* Source code view: function entry nonzero count*/ |
@@ -2615,14 +3020,15 @@ sub write_css_file() |
padding-right: 10px; |
background-color: #DAE7FE; |
font-weight: bold; |
+ font-family: sans-serif; |
} |
/* Source code view: source code format */ |
- /* Source code view: source code format */ |
pre.source |
{ |
font-family: monospace; |
white-space: pre; |
+ margin-top: 2px; |
} |
/* Source code view: line number format */ |
@@ -2660,7 +3066,7 @@ sub write_css_file() |
padding-left: 10px; |
padding-right: 10px; |
padding-bottom: 2px; |
- background-color: #FF0000; |
+ background-color: #FF6230; |
} |
/* Source code view (function table): standard link - visited format */ |
@@ -2678,38 +3084,122 @@ sub write_css_file() |
background-color: #B5F7AF; |
} |
- /* Source code view: format for DiffCov legend */ |
- span.LegendDiffCov |
+ /* Source code view: format for branches which were executed |
+ * and taken */ |
+ span.branchCov |
{ |
- text-align: center; |
- padding-left: 10px; |
- padding-right: 10px; |
- background-color: #B5F7AF; |
+ background-color: #CAD7FE; |
} |
-END_OF_CSS |
- ; |
- |
- # ************************************************************* |
+ /* Source code view: format for branches which were executed |
+ * but not taken */ |
+ span.branchNoCov |
+ { |
+ background-color: #FF6230; |
+ } |
- # Remove leading tab from all lines |
- $css_data =~ s/^\t//gm; |
+ /* Source code view: format for branches which were not executed */ |
+ span.branchNoExec |
+ { |
+ background-color: #FF6230; |
+ } |
- print(CSS_HANDLE $css_data); |
+ /* Source code view: format for the source code heading line */ |
+ pre.sourceHeading |
+ { |
+ white-space: pre; |
+ font-family: monospace; |
+ font-weight: bold; |
+ margin: 0px; |
+ } |
- close(CSS_HANDLE); |
-} |
+ /* All views: header legend value for low rate */ |
+ td.headerValueLegL |
+ { |
+ font-family: sans-serif; |
+ text-align: center; |
+ white-space: nowrap; |
+ padding-left: 4px; |
+ padding-right: 2px; |
+ background-color: #FF0000; |
+ font-size: 80%; |
+ } |
+ /* All views: header legend value for med rate */ |
+ td.headerValueLegM |
+ { |
+ font-family: sans-serif; |
+ text-align: center; |
+ white-space: nowrap; |
+ padding-left: 2px; |
+ padding-right: 2px; |
+ background-color: #FFEA20; |
+ font-size: 80%; |
+ } |
-# |
-# get_bar_graph_code(base_dir, cover_found, cover_hit) |
-# |
+ /* All views: header legend value for hi rate */ |
+ td.headerValueLegH |
+ { |
+ font-family: sans-serif; |
+ text-align: center; |
+ white-space: nowrap; |
+ padding-left: 2px; |
+ padding-right: 4px; |
+ background-color: #A7FC9D; |
+ font-size: 80%; |
+ } |
+ |
+ /* All views except source code view: legend format for low coverage */ |
+ span.coverLegendCovLo |
+ { |
+ padding-left: 10px; |
+ padding-right: 10px; |
+ padding-top: 2px; |
+ background-color: #FF0000; |
+ } |
+ |
+ /* All views except source code view: legend format for med coverage */ |
+ span.coverLegendCovMed |
+ { |
+ padding-left: 10px; |
+ padding-right: 10px; |
+ padding-top: 2px; |
+ background-color: #FFEA20; |
+ } |
+ |
+ /* All views except source code view: legend format for hi coverage */ |
+ span.coverLegendCovHi |
+ { |
+ padding-left: 10px; |
+ padding-right: 10px; |
+ padding-top: 2px; |
+ background-color: #A7FC9D; |
+ } |
+END_OF_CSS |
+ ; |
+ |
+ # ************************************************************* |
+ |
+ |
+ # Remove leading tab from all lines |
+ $css_data =~ s/^\t//gm; |
+ |
+ print(CSS_HANDLE $css_data); |
+ |
+ close(CSS_HANDLE); |
+} |
+ |
+ |
+# |
+# get_bar_graph_code(base_dir, cover_found, cover_hit) |
+# |
# Return a string containing HTML code which implements a bar graph display |
# for a coverage rate of cover_hit * 100 / cover_found. |
# |
sub get_bar_graph_code($$$) |
{ |
+ my ($base_dir, $found, $hit) = @_; |
my $rate; |
my $alt; |
my $width; |
@@ -2720,13 +3210,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) |
@@ -2775,7 +3264,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) { |
@@ -2853,104 +3342,40 @@ END_OF_HTML |
# |
-# write_header_line(filehandle, type, additional params..) |
+# write_header_line(handle, content) |
# |
-# Write a header line. |
+# Write a header line with the specified table contents. |
# |
-sub write_header_line(*$@) |
+sub write_header_line(*@) |
{ |
- my $HANDLE = shift; |
- my $type = shift; |
- my @args = @_; |
- |
- # Reduce indentation by using gotos |
- if ($type eq 0) { |
- goto header; |
- } elsif ($type eq 1) { |
- goto body; |
- } elsif ($type eq 2) { |
- goto legend_dir; |
- } elsif ($type eq 3) { |
- goto legend_source; |
- } elsif ($type eq 4) { |
- goto half_body; |
- } |
- |
-header: |
- # ************************************************************* |
- write_html($HANDLE, <<END_OF_HTML); |
- <tr> |
- <td width="5%"></td> |
- <td width="10%" class="headerItem">$args[0]</td> |
- <td width="35%" class="headerValue">$args[1]</td> |
- <td width="10%"></td> |
- <td width="10%" class="headerCovTableHead">$args[2]</td> |
- <td width="10%" class="headerCovTableHead">$args[3]</td> |
- <td width="15%" class="headerCovTableHead">$args[4]</td> |
- <td width="5%"></td> |
- </tr> |
-END_OF_HTML |
- # ************************************************************* |
- return; |
+ my ($handle, @content) = @_; |
+ my $entry; |
-body: |
- # ************************************************************* |
- write_html($HANDLE, <<END_OF_HTML); |
- <tr> |
- <td></td> |
- <td class="headerItem">$args[0]</td> |
- <td class="headerValue">$args[1]</td> |
- <td class="headerItem">$args[2]</td> |
- <td class="headerCovTableEntry">$args[3]</td> |
- <td class="headerCovTableEntry">$args[4]</td> |
- <td class="headerCovTableEntry$args[5]">$args[6]</td> |
- </tr> |
-END_OF_HTML |
- # ************************************************************* |
- return; |
- |
-half_body: |
- # ************************************************************* |
- write_html($HANDLE, <<END_OF_HTML); |
- <tr> |
- <td></td> |
- <td class="headerItem">$args[0]</td> |
- <td class="headerValue">$args[1]</td> |
- </tr> |
-END_OF_HTML |
- # ************************************************************* |
- return; |
+ write_html($handle, " <tr>\n"); |
+ foreach $entry (@content) { |
+ my ($width, $class, $text, $colspan) = @{$entry}; |
-legend_dir: |
- # ************************************************************* |
- write_html($HANDLE, <<END_OF_HTML); |
- <tr> |
- <td></td> |
- <td class="headerItemLeg">$args[0]</td> |
- <td class="headerValueLeg"> |
-$args[1] </td> |
- <td></td> |
- <td class="headerValueLeg" colspan=3> |
-$args[2] </td> |
- </tr> |
-END_OF_HTML |
- # ************************************************************* |
- return; |
- |
-legend_source: |
- # ************************************************************* |
- write_html($HANDLE, <<END_OF_HTML); |
- <tr> |
- <td></td> |
- <td class="headerItem">$args[0]</td> |
- <td class="headerValueLeg" colspan=5> |
- <span class="coverLegendNoCov">$args[1]</span> |
- <span class="coverLegendCov">$args[2]</span> |
- </td> |
- </tr> |
-END_OF_HTML |
- # ************************************************************* |
+ if (defined($width)) { |
+ $width = " width=\"$width\""; |
+ } else { |
+ $width = ""; |
+ } |
+ if (defined($class)) { |
+ $class = " class=\"$class\""; |
+ } else { |
+ $class = ""; |
+ } |
+ if (defined($colspan)) { |
+ $colspan = " colspan=\"$colspan\""; |
+ } else { |
+ $colspan = ""; |
+ } |
+ $text = "" if (!defined($text)); |
+ write_html($handle, |
+ " <td$width$class$colspan>$text</td>\n"); |
+ } |
+ write_html($handle, " </tr>\n"); |
} |
@@ -2965,10 +3390,11 @@ sub write_header_epilog(*$) |
# ************************************************************* |
write_html($_[0], <<END_OF_HTML) |
- <tr><td><img src="$_[1]glass.png" width=3 height=3 alt=""></td></tr> |
+ <tr><td><img src="$_[1]glass.png" width=3 height=3 alt=""></td></tr> |
</table> |
</td> |
</tr> |
+ |
<tr><td class="ruler"><img src="$_[1]glass.png" width=3 height=3 alt=""></td></tr> |
</table> |
@@ -2980,244 +3406,170 @@ END_OF_HTML |
# |
-# write_file_table_prolog(filehandle, file_heading, lines_heading, func_heading) |
+# write_file_table_prolog(handle, file_heading, ([heading, num_cols], ...)) |
# |
# Write heading for file table. |
# |
-sub write_file_table_prolog(*$$$) |
+sub write_file_table_prolog(*$@) |
{ |
- # ************************************************************* |
+ my ($handle, $file_heading, @columns) = @_; |
+ my $num_columns = 0; |
+ my $file_width; |
+ my $col; |
+ my $width; |
- if ($func_coverage) |
- { |
- write_html($_[0], <<END_OF_HTML) |
- <center> |
- <table width="80%" cellpadding=1 cellspacing=1 border=0> |
+ $width = 20 if (scalar(@columns) == 1); |
+ $width = 10 if (scalar(@columns) == 2); |
+ $width = 8 if (scalar(@columns) > 2); |
- <tr> |
- <td width="45%"><br></td> |
- <td width="15%"></td> |
- <td width="10%"></td> |
- <td width="10%"></td> |
- <td width="10%"></td> |
- <td width="10%"></td> |
- </tr> |
+ foreach $col (@columns) { |
+ my ($heading, $cols) = @{$col}; |
- <tr> |
- <td class="tableHead">$_[1]</td> |
- <td class="tableHead" colspan=3>$_[2]</td> |
- <td class="tableHead" colspan=2>$_[3]</td> |
- </tr> |
+ $num_columns += $cols; |
+ } |
+ $file_width = 100 - $num_columns * $width; |
-END_OF_HTML |
- ; |
- } |
- else |
- { |
- write_html($_[0], <<END_OF_HTML) |
+ # Table definition |
+ write_html($handle, <<END_OF_HTML); |
<center> |
<table width="80%" cellpadding=1 cellspacing=1 border=0> |
<tr> |
- <td width="50%"><br></td> |
- <td width="15%"></td> |
- <td width="15%"></td> |
- <td width="20%"></td> |
+ <td width="$file_width%"><br></td> |
+END_OF_HTML |
+ # Empty first row |
+ foreach $col (@columns) { |
+ my ($heading, $cols) = @{$col}; |
+ |
+ while ($cols-- > 0) { |
+ write_html($handle, <<END_OF_HTML); |
+ <td width="$width%"></td> |
+END_OF_HTML |
+ } |
+ } |
+ # Next row |
+ write_html($handle, <<END_OF_HTML); |
</tr> |
<tr> |
- <td class="tableHead">$_[1]</td> |
- <td class="tableHead" colspan=3>$_[2]</td> |
+ <td class="tableHead">$file_heading</td> |
+END_OF_HTML |
+ # Heading row |
+ foreach $col (@columns) { |
+ my ($heading, $cols) = @{$col}; |
+ my $colspan = ""; |
+ |
+ $colspan = " colspan=$cols" if ($cols > 1); |
+ write_html($handle, <<END_OF_HTML); |
+ <td class="tableHead"$colspan>$heading</td> |
+END_OF_HTML |
+ } |
+ write_html($handle, <<END_OF_HTML); |
</tr> |
- |
END_OF_HTML |
- ; |
- } |
- |
- # ************************************************************* |
} |
-# |
-# write_file_table_entry(filehandle, cover_filename, cover_bar_graph, |
-# cover_found, cover_hit, fn_found, fn_hit, |
-# page_link, func_link) |
+# write_file_table_entry(handle, base_dir, filename, page_link, |
+# ([ found, hit, med_limit, hi_limit, graph ], ..) |
# |
# Write an entry of the file table. |
# |
-sub write_file_table_entry(*$$$$$$$) |
+sub write_file_table_entry(*$$$@) |
{ |
- local *HANDLE = shift; |
- my ($filename, $bar_graph, $found, $hit, $fn_found, $fn_hit, |
- $page_link) = @_; |
- my $rate; |
- my $rate_string; |
- my $funcs_string; |
- my $class_lines = "Lo"; |
- my $class_funcs = "Hi"; |
+ 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; |
} |
- # Get line coverage rate |
- if ($found > 0) |
- { |
- $rate = $hit * 100 / $found; |
- $rate_string = sprintf("%.1f", $rate)." %"; |
- |
- $class_lines = $rate_name[classify_rate($found, $hit, |
- $med_limit, $hi_limit)]; |
- } |
- else |
- { |
- $rate_string = "-"; |
- } |
- |
- # Get function coverage rate |
- if ($fn_found > 0) |
- { |
- $rate = $fn_hit * 100 / $fn_found; |
- $class_funcs = $rate_name[classify_rate($fn_found, $fn_hit, |
- $fn_med_limit, $fn_hi_limit)]; |
- $funcs_string = sprintf("%.1f", $rate)." %"; |
- } |
- else |
- { |
- # Define 0 of 0 functions as 100% |
- $rate = 100; |
- $funcs_string = "-"; |
- } |
- |
- # ************************************************************* |
- |
- write_html(*HANDLE, <<END_OF_HTML) |
+ # First column: filename |
+ write_html($handle, <<END_OF_HTML); |
<tr> |
<td class="coverFile">$file_code</td> |
+END_OF_HTML |
+ # Columns as defined |
+ foreach $entry (@entries) { |
+ my ($found, $hit, $med, $hi, $graph) = @{$entry}; |
+ my $bar_graph; |
+ my $class; |
+ my $rate; |
+ |
+ # Generate bar graph if requested |
+ if ($graph) { |
+ $bar_graph = get_bar_graph_code($base_dir, $found, |
+ $hit); |
+ write_html($handle, <<END_OF_HTML); |
<td class="coverBar" align="center"> |
$bar_graph |
</td> |
- <td class="coverPer$class_lines">$rate_string</td> |
- <td class="coverNum$class_lines">$hit / $found</td> |
END_OF_HTML |
- ; |
- |
- if ($func_coverage) |
- { |
- write_html(*HANDLE, <<END_OF_HTML) |
- <td class="coverPer$class_funcs">$funcs_string</td> |
- <td class="coverNum$class_funcs">$fn_hit / $fn_found</td> |
-END_OF_HTML |
- ; |
- } |
- write_html(*HANDLE, <<END_OF_HTML) |
- </tr> |
+ } |
+ # Get rate color and text |
+ if ($found == 0) { |
+ $rate = "-"; |
+ $class = "Hi"; |
+ } else { |
+ $rate = rate($hit, $found, " %"); |
+ $class = $rate_name[classify_rate($found, $hit, |
+ $med, $hi)]; |
+ } |
+ write_html($handle, <<END_OF_HTML); |
+ <td class="coverPer$class">$rate</td> |
+ <td class="coverNum$class">$hit / $found</td> |
END_OF_HTML |
- ; |
- |
- # ************************************************************* |
-} |
- |
- |
-# |
-# write_file_table_detail_heading(filehandle, left_heading, right_heading) |
-# |
-# Write heading for detail section in file table. |
-# |
- |
-sub write_file_table_detail_heading(*$$$) |
-{ |
- my $func_rows = ""; |
- |
- if ($func_coverage) |
- { |
- $func_rows = "<td class=\"testLinesHead\" colspan=2>$_[3]</td>"; |
- } |
- |
- # ************************************************************* |
- write_html($_[0], <<END_OF_HTML) |
- <tr> |
- <td class="testNameHead" colspan=2>$_[1]</td> |
- <td class="testLinesHead" colspan=2>$_[2]</td> |
- $func_rows |
+ } |
+ # End of row |
+ write_html($handle, <<END_OF_HTML); |
</tr> |
- |
END_OF_HTML |
- ; |
- |
- # ************************************************************* |
} |
# |
-# write_file_table_detail_entry(filehandle, test_name, |
-# cover_found, cover_hit, func_found, func_hit) |
+# write_file_table_detail_entry(filehandle, test_name, ([found, hit], ...)) |
# |
# Write entry for detail section in file table. |
# |
-sub write_file_table_detail_entry(*$$$$$) |
+sub write_file_table_detail_entry(*$@) |
{ |
- my $rate; |
- my $func_rate; |
- my $name = $_[1]; |
- |
- if ($_[2]>0) |
- { |
- $rate = sprintf("%.1f", $_[3]*100/$_[2])." %"; |
- } |
- else |
- { |
- $rate = "-"; |
- } |
- |
- if ($_[4]>0) |
- { |
- $func_rate = sprintf("%.1f", $_[5]*100/$_[4])." %"; |
- } |
- else |
- { |
- $func_rate = "-"; |
- } |
+ my ($handle, $test, @entries) = @_; |
+ my $entry; |
- if ($name =~ /^(.*),diff$/) |
- { |
- $name = $1." (converted)"; |
- } |
- |
- if ($name eq "") |
- { |
- $name = "<span style=\"font-style:italic\"><unnamed></span>"; |
+ if ($test eq "") { |
+ $test = "<span style=\"font-style:italic\"><unnamed></span>"; |
+ } elsif ($test =~ /^(.*),diff$/) { |
+ $test = $1." (converted)"; |
} |
- |
- # ************************************************************* |
- |
- write_html($_[0], <<END_OF_HTML) |
+ # Testname |
+ write_html($handle, <<END_OF_HTML); |
<tr> |
- <td class="testName" colspan=2>$name</td> |
- <td class="testPer">$rate</td> |
- <td class="testNum">$_[3] / $_[2] lines</td> |
+ <td class="testName" colspan=2>$test</td> |
END_OF_HTML |
- ; |
- if ($func_coverage) |
- { |
- write_html($_[0], <<END_OF_HTML) |
- <td class="testPer">$func_rate</td> |
- <td class="testNum">$_[5] / $_[4]</td> |
+ # Test data |
+ foreach $entry (@entries) { |
+ my ($found, $hit) = @{$entry}; |
+ my $rate = rate($hit, $found, " %"); |
+ |
+ write_html($handle, <<END_OF_HTML); |
+ <td class="testPer">$rate</td> |
+ <td class="testNum">$hit / $found</td> |
END_OF_HTML |
- ; |
- } |
- write_html($_[0], <<END_OF_HTML) |
+ } |
+ |
+ write_html($handle, <<END_OF_HTML); |
</tr> |
END_OF_HTML |
- ; |
# ************************************************************* |
} |
@@ -3322,6 +3674,17 @@ END_OF_HTML |
} |
+sub fmt_centered($$) |
+{ |
+ my ($width, $text) = @_; |
+ my $w0 = length($text); |
+ my $w1 = int(($width - $w0) / 2); |
+ my $w2 = $width - $w0 - $w1; |
+ |
+ return (" "x$w1).$text.(" "x$w2); |
+} |
+ |
+ |
# |
# write_source_prolog(filehandle) |
# |
@@ -3330,6 +3693,15 @@ END_OF_HTML |
sub write_source_prolog(*) |
{ |
+ my $lineno_heading = " "; |
+ my $branch_heading = ""; |
+ my $line_heading = fmt_centered($line_field_width, "Line data"); |
+ my $source_heading = " Source code"; |
+ |
+ if ($br_coverage) { |
+ $branch_heading = fmt_centered($br_field_width, "Branch data"). |
+ " "; |
+ } |
# ************************************************************* |
write_html($_[0], <<END_OF_HTML) |
@@ -3338,7 +3710,9 @@ sub write_source_prolog(*) |
<td><br></td> |
</tr> |
<tr> |
- <td><pre class="source"> |
+ <td> |
+<pre class="sourceHeading">${lineno_heading}${branch_heading}${line_heading} ${source_heading}</pre> |
+<pre class="source"> |
END_OF_HTML |
; |
@@ -3347,51 +3721,251 @@ END_OF_HTML |
# |
+# get_branch_blocks(brdata) |
+# |
+# Group branches that belong to the same basic block. |
+# |
+# Returns: [block1, block2, ...] |
+# block: [branch1, branch2, ...] |
+# branch: [block_num, branch_num, taken_count, text_length, open, close] |
+# |
+ |
+sub get_branch_blocks($) |
+{ |
+ my ($brdata) = @_; |
+ my $last_block_num; |
+ my $block = []; |
+ my @blocks; |
+ my $i; |
+ my $num = br_ivec_len($brdata); |
+ |
+ # Group branches |
+ for ($i = 0; $i < $num; $i++) { |
+ my ($block_num, $branch, $taken) = br_ivec_get($brdata, $i); |
+ my $br; |
+ |
+ if (defined($last_block_num) && $block_num != $last_block_num) { |
+ push(@blocks, $block); |
+ $block = []; |
+ } |
+ $br = [$block_num, $branch, $taken, 3, 0, 0]; |
+ push(@{$block}, $br); |
+ $last_block_num = $block_num; |
+ } |
+ push(@blocks, $block) if (scalar(@{$block}) > 0); |
+ |
+ # Add braces to first and last branch in group |
+ foreach $block (@blocks) { |
+ $block->[0]->[$BR_OPEN] = 1; |
+ $block->[0]->[$BR_LEN]++; |
+ $block->[scalar(@{$block}) - 1]->[$BR_CLOSE] = 1; |
+ $block->[scalar(@{$block}) - 1]->[$BR_LEN]++; |
+ } |
+ |
+ return @blocks; |
+} |
+ |
+# |
+# get_block_len(block) |
+# |
+# Calculate total text length of all branches in a block of branches. |
+# |
+ |
+sub get_block_len($) |
+{ |
+ my ($block) = @_; |
+ my $len = 0; |
+ my $branch; |
+ |
+ foreach $branch (@{$block}) { |
+ $len += $branch->[$BR_LEN]; |
+ } |
+ |
+ return $len; |
+} |
+ |
+ |
+# |
+# get_branch_html(brdata) |
+# |
+# Return a list of HTML lines which represent the specified branch coverage |
+# data in source code view. |
+# |
+ |
+sub get_branch_html($) |
+{ |
+ my ($brdata) = @_; |
+ my @blocks = get_branch_blocks($brdata); |
+ my $block; |
+ my $branch; |
+ my $line_len = 0; |
+ my $line = []; # [branch2|" ", branch|" ", ...] |
+ my @lines; # [line1, line2, ...] |
+ my @result; |
+ |
+ # Distribute blocks to lines |
+ foreach $block (@blocks) { |
+ my $block_len = get_block_len($block); |
+ |
+ # Does this block fit into the current line? |
+ if ($line_len + $block_len <= $br_field_width) { |
+ # Add it |
+ $line_len += $block_len; |
+ push(@{$line}, @{$block}); |
+ next; |
+ } elsif ($block_len <= $br_field_width) { |
+ # It would fit if the line was empty - add it to new |
+ # line |
+ push(@lines, $line); |
+ $line_len = $block_len; |
+ $line = [ @{$block} ]; |
+ next; |
+ } |
+ # Split the block into several lines |
+ foreach $branch (@{$block}) { |
+ if ($line_len + $branch->[$BR_LEN] >= $br_field_width) { |
+ # Start a new line |
+ if (($line_len + 1 <= $br_field_width) && |
+ scalar(@{$line}) > 0 && |
+ !$line->[scalar(@$line) - 1]->[$BR_CLOSE]) { |
+ # Try to align branch symbols to be in |
+ # one # row |
+ push(@{$line}, " "); |
+ } |
+ push(@lines, $line); |
+ $line_len = 0; |
+ $line = []; |
+ } |
+ push(@{$line}, $branch); |
+ $line_len += $branch->[$BR_LEN]; |
+ } |
+ } |
+ push(@lines, $line); |
+ |
+ # Convert to HTML |
+ foreach $line (@lines) { |
+ my $current = ""; |
+ my $current_len = 0; |
+ |
+ foreach $branch (@$line) { |
+ # Skip alignment space |
+ if ($branch eq " ") { |
+ $current .= " "; |
+ $current_len++; |
+ next; |
+ } |
+ |
+ my ($block_num, $br_num, $taken, $len, $open, $close) = |
+ @{$branch}; |
+ my $class; |
+ my $title; |
+ my $text; |
+ |
+ if ($taken eq '-') { |
+ $class = "branchNoExec"; |
+ $text = " # "; |
+ $title = "Branch $br_num was not executed"; |
+ } elsif ($taken == 0) { |
+ $class = "branchNoCov"; |
+ $text = " - "; |
+ $title = "Branch $br_num was not taken"; |
+ } else { |
+ $class = "branchCov"; |
+ $text = " + "; |
+ $title = "Branch $br_num was taken $taken ". |
+ "time"; |
+ $title .= "s" if ($taken > 1); |
+ } |
+ $current .= "[" if ($open); |
+ $current .= "<span class=\"$class\" title=\"$title\">"; |
+ $current .= $text."</span>"; |
+ $current .= "]" if ($close); |
+ $current_len += $len; |
+ } |
+ |
+ # Right-align result text |
+ if ($current_len < $br_field_width) { |
+ $current = (" "x($br_field_width - $current_len)). |
+ $current; |
+ } |
+ push(@result, $current); |
+ } |
+ |
+ return @result; |
+} |
+ |
+ |
+# |
+# format_count(count, width) |
+# |
+# Return a right-aligned representation of count that fits in width characters. |
+# |
+ |
+sub format_count($$) |
+{ |
+ my ($count, $width) = @_; |
+ my $result; |
+ my $exp; |
+ |
+ $result = sprintf("%*.0f", $width, $count); |
+ while (length($result) > $width) { |
+ last if ($count < 10); |
+ $exp++; |
+ $count = int($count/10); |
+ $result = sprintf("%*s", $width, ">$count*10^$exp"); |
+ } |
+ return $result; |
+} |
+ |
+# |
# write_source_line(filehandle, line_num, source, hit_count, converted, |
-# add_anchor) |
+# brdata, add_anchor) |
# |
# Write formatted source code line. Return a line in a format as needed |
# by gen_png() |
# |
-sub write_source_line(*$$$$$) |
+sub write_source_line(*$$$$$$) |
{ |
+ my ($handle, $line, $source, $count, $converted, $brdata, |
+ $add_anchor) = @_; |
my $source_format; |
- my $count; |
+ my $count_format; |
my $result; |
my $anchor_start = ""; |
my $anchor_end = ""; |
+ my $count_field_width = $line_field_width - 1; |
+ my @br_html; |
+ my $html; |
- if (!(defined$_[3])) |
- { |
+ # Get branch HTML data for this line |
+ @br_html = get_branch_html($brdata) if ($br_coverage); |
+ |
+ if (!defined($count)) { |
$result = ""; |
$source_format = ""; |
- $count = " "x15; |
+ $count_format = " "x$count_field_width; |
} |
- elsif ($_[3] == 0) |
- { |
- $result = $_[3]; |
+ elsif ($count == 0) { |
+ $result = $count; |
$source_format = '<span class="lineNoCov">'; |
- $count = sprintf("%15d", $_[3]); |
+ $count_format = format_count($count, $count_field_width); |
} |
- elsif ($_[4] && defined($highlight)) |
- { |
- $result = "*".$_[3]; |
+ elsif ($converted && defined($highlight)) { |
+ $result = "*".$count; |
$source_format = '<span class="lineDiffCov">'; |
- $count = sprintf("%15d", $_[3]); |
+ $count_format = format_count($count, $count_field_width); |
} |
- else |
- { |
- $result = $_[3]; |
+ else { |
+ $result = $count; |
$source_format = '<span class="lineCov">'; |
- $count = sprintf("%15d", $_[3]); |
+ $count_format = format_count($count, $count_field_width); |
} |
- |
- $result .= ":".$_[2]; |
+ $result .= ":".$source; |
# Write out a line number navigation anchor every $nav_resolution |
# lines if necessary |
- if ($_[5]) |
+ if ($add_anchor) |
{ |
$anchor_start = "<a name=\"$_[1]\">"; |
$anchor_end = "</a>"; |
@@ -3400,13 +3974,23 @@ sub write_source_line(*$$$$$) |
# ************************************************************* |
- write_html($_[0], |
- $anchor_start. |
- '<span class="lineNum">'.sprintf("%8d", $_[1]). |
- " </span>$source_format$count : ". |
- escape_html($_[2]).($source_format?"</span>":""). |
- $anchor_end."\n"); |
- |
+ $html = $anchor_start; |
+ $html .= "<span class=\"lineNum\">".sprintf("%8d", $line)." </span>"; |
+ $html .= shift(@br_html).":" if ($br_coverage); |
+ $html .= "$source_format$count_format : "; |
+ $html .= escape_html($source); |
+ $html .= "</span>" if ($source_format); |
+ $html .= $anchor_end."\n"; |
+ |
+ write_html($handle, $html); |
+ |
+ if ($br_coverage) { |
+ # Add lines for overlong branch information |
+ foreach (@br_html) { |
+ write_html($handle, "<span class=\"lineNum\">". |
+ " </span>$_\n"); |
+ } |
+ } |
# ************************************************************* |
return($result); |
@@ -3460,8 +4044,8 @@ sub write_html_epilog(*$;$) |
write_html($_[0], <<END_OF_HTML) |
<table width="100%" border=0 cellspacing=0 cellpadding=0> |
- <tr><td class="ruler"><img src="$_[1]glass.png" width=3 height=3 alt=""></td></tr> |
- <tr><td class="versionInfo">Generated by: <a href="$lcov_url"$break_code>$lcov_version</a></td></tr> |
+ <tr><td class="ruler"><img src="$_[1]glass.png" width=3 height=3 alt=""></td></tr> |
+ <tr><td class="versionInfo">Generated by: <a href="$lcov_url"$break_code>$lcov_version</a></td></tr> |
</table> |
<br> |
END_OF_HTML |
@@ -3491,7 +4075,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> |
@@ -3554,7 +4138,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> |
@@ -3601,51 +4185,14 @@ END_OF_HTML |
} |
-# rate_to_col(found, hit) |
-# |
-# Return Lo, Med or Hi, depending on the coverage rate. |
-# |
- |
-sub rate_to_col($$) |
+sub max($$) |
{ |
- my ($found, $hit) = @_; |
- my $rate; |
- |
- if ($found == 0) { |
- return "Hi"; |
- } |
- $rate = 100 * $hit / $found; |
- if ($rate < $med_limit) { |
- return "Lo"; |
- } elsif ($rate < $hi_limit) { |
- return "Med"; |
- } |
- return "Hi"; |
-} |
- |
-# format_rate(found, hit) |
-# |
-# Return formatted percent string for coverage rate. |
-# |
+ my ($a, $b) = @_; |
-sub format_rate($$) |
-{ |
- return $_[0] == 0 ? "-" : sprintf("%.1f", $_[1] * 100 / $_[0])." %"; |
+ return $a if ($a > $b); |
+ return $b; |
} |
-sub get_legend_code($$$) |
-{ |
- my ($text, $med, $hi) = @_; |
- my $result; |
- |
- $result = <<EOF; |
- $text<br> |
- <span class="coverLegendLo">0% to $med%</span> |
- <span class="coverLegendMed">$med% to $hi%</span> |
- <span class="coverLegendHi">$hi% to 100%</span> |
-EOF |
- return $result; |
-} |
# |
# write_header(filehandle, type, trunc_file_name, rel_file_name, lines_found, |
@@ -3656,7 +4203,7 @@ EOF |
# header, test case description header, function view header) |
# |
-sub write_header(*$$$$$$$$) |
+sub write_header(*$$$$$$$$$$) |
{ |
local *HTML_HANDLE = $_[0]; |
my $type = $_[1]; |
@@ -3666,32 +4213,43 @@ sub write_header(*$$$$$$$$) |
my $lines_hit = $_[5]; |
my $fn_found = $_[6]; |
my $fn_hit = $_[7]; |
- my $sort_type = $_[8]; |
+ my $br_found = $_[8]; |
+ my $br_hit = $_[9]; |
+ my $sort_type = $_[10]; |
my $base_dir; |
my $view; |
my $test; |
my $base_name; |
+ my $style; |
+ my $rate; |
+ my @row_left; |
+ my @row_right; |
+ my $num_rows; |
+ my $i; |
+ my $esc_trunc_name = escape_html($trunc_name); |
$base_name = basename($rel_filename); |
# Prepare text for "current view" field |
- if ($type == 0) |
+ if ($type == $HDR_DIR) |
{ |
# Main overview |
$base_dir = ""; |
$view = $overview_title; |
} |
- elsif ($type == 1) |
+ elsif ($type == $HDR_FILE) |
{ |
# 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 == 2 || $type == 4) |
+ 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) |
@@ -3701,26 +4259,28 @@ 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) { |
- if ($type == 2) { |
+ $view .= "<span style=\"font-size: 80%;\">"; |
+ if ($type == $HDR_SOURCE) { |
$view .= " (source / <a href=\"$base_name.func.$html_ext\">functions</a>)"; |
- } elsif ($type == 4) { |
+ } elsif ($type == $HDR_FUNC) { |
$view .= " (<a href=\"$base_name.gcov.$html_ext\">source</a> / functions)"; |
} |
+ $view .= "</span>"; |
} |
} |
- elsif ($type == 3) |
+ elsif ($type == $HDR_TESTDESC) |
{ |
# Test description header |
$base_dir = ""; |
@@ -3732,84 +4292,126 @@ sub write_header(*$$$$$$$$) |
$test = escape_html($test_title); |
# Append link to test description page if available |
- if (%test_description && ($type != 3)) |
+ if (%test_description && ($type != $HDR_TESTDESC)) |
{ |
- if ($frames && ($type == 2 || $type == 4)) |
+ if ($frames && ($type == $HDR_SOURCE || $type == $HDR_FUNC)) |
{ |
# Need to break frameset when clicking this link |
- $test .= " ( <a href=\"$base_dir". |
+ $test .= " ( <span style=\"font-size:80%;\">". |
+ "<a href=\"$base_dir". |
"descriptions.$html_ext\" target=\"_parent\">". |
- "view descriptions</a> )"; |
+ "view descriptions</a></span> )"; |
} |
else |
{ |
- $test .= " ( <a href=\"$base_dir". |
+ $test .= " ( <span style=\"font-size:80%;\">". |
+ "<a href=\"$base_dir". |
"descriptions.$html_ext\">". |
- "view descriptions</a> )"; |
+ "view descriptions</a></span> )"; |
} |
} |
# Write header |
write_header_prolog(*HTML_HANDLE, $base_dir); |
- write_header_line(*HTML_HANDLE, 0, "Current view:", $view, |
- "Found", "Hit", "Coverage"); |
- write_header_line(*HTML_HANDLE, 1, "Test:", $test, "Lines:", |
- $lines_found, $lines_hit, |
- $rate_name[classify_rate($lines_found, $lines_hit, |
- $med_limit, $hi_limit)], |
- format_rate($lines_found, $lines_hit)); |
- if ($func_coverage) { |
- write_header_line(*HTML_HANDLE, 1, "Date:", $date, "Functions:", |
- $fn_found, $fn_hit, |
- $rate_name[classify_rate($fn_found, |
- $fn_hit, |
- $fn_med_limit, |
- $fn_hi_limit)], |
- format_rate($fn_found, $fn_hit)); |
+ |
+ # Left row |
+ push(@row_left, [[ "10%", "headerItem", "Current view:" ], |
+ [ "35%", "headerValue", $view ]]); |
+ push(@row_left, [[undef, "headerItem", "Test:"], |
+ [undef, "headerValue", $test]]); |
+ push(@row_left, [[undef, "headerItem", "Date:"], |
+ [undef, "headerValue", $date]]); |
+ |
+ # Right row |
+ if ($legend && ($type == $HDR_SOURCE || $type == $HDR_FUNC)) { |
+ my $text = <<END_OF_HTML; |
+ Lines: |
+ <span class="coverLegendCov">hit</span> |
+ <span class="coverLegendNoCov">not hit</span> |
+END_OF_HTML |
+ if ($br_coverage) { |
+ $text .= <<END_OF_HTML; |
+ | Branches: |
+ <span class="coverLegendCov">+</span> taken |
+ <span class="coverLegendNoCov">-</span> not taken |
+ <span class="coverLegendNoCov">#</span> not executed |
+END_OF_HTML |
+ } |
+ push(@row_left, [[undef, "headerItem", "Legend:"], |
+ [undef, "headerValueLeg", $text]]); |
+ } elsif ($legend && ($type != $HDR_TESTDESC)) { |
+ my $text = <<END_OF_HTML; |
+ Rating: |
+ <span class="coverLegendCovLo" title="Coverage rates below $med_limit % are classified as low">low: < $med_limit %</span> |
+ <span class="coverLegendCovMed" title="Coverage rates between $med_limit % and $hi_limit % are classified as medium">medium: >= $med_limit %</span> |
+ <span class="coverLegendCovHi" title="Coverage rates of $hi_limit % and more are classified as high">high: >= $hi_limit %</span> |
+END_OF_HTML |
+ push(@row_left, [[undef, "headerItem", "Legend:"], |
+ [undef, "headerValueLeg", $text]]); |
+ } |
+ if ($type == $HDR_TESTDESC) { |
+ push(@row_right, [[ "55%" ]]); |
} else { |
- write_header_line(*HTML_HANDLE, 4, "Date:", $date); |
- } |
- if ($legend) { |
- if ($type == 0 || $type == 1) { |
- my $line_code = get_legend_code("Line coverage:", |
- $med_limit, $hi_limit); |
- my $func_code = ""; |
- |
- if ($func_coverage) { |
- $func_code = get_legend_code( |
- "Function coverage:", |
- $fn_med_limit, |
- $fn_hi_limit); |
- } |
- write_header_line(*HTML_HANDLE, 2, "Colors:", |
- $line_code, $func_code); |
- } elsif ($type == 2 || $type == 4) { |
- write_header_line(*HTML_HANDLE, 3, "Colors:", |
- "not hit", "hit"); |
+ push(@row_right, [["15%", undef, undef ], |
+ ["10%", "headerCovTableHead", "Hit" ], |
+ ["10%", "headerCovTableHead", "Total" ], |
+ ["15%", "headerCovTableHead", "Coverage"]]); |
+ } |
+ # Line coverage |
+ $style = $rate_name[classify_rate($lines_found, $lines_hit, |
+ $med_limit, $hi_limit)]; |
+ $rate = rate($lines_hit, $lines_found, " %"); |
+ push(@row_right, [[undef, "headerItem", "Lines:"], |
+ [undef, "headerCovTableEntry", $lines_hit], |
+ [undef, "headerCovTableEntry", $lines_found], |
+ [undef, "headerCovTableEntry$style", $rate]]) |
+ if ($type != $HDR_TESTDESC); |
+ # Function coverage |
+ if ($func_coverage) { |
+ $style = $rate_name[classify_rate($fn_found, $fn_hit, |
+ $fn_med_limit, $fn_hi_limit)]; |
+ $rate = rate($fn_hit, $fn_found, " %"); |
+ push(@row_right, [[undef, "headerItem", "Functions:"], |
+ [undef, "headerCovTableEntry", $fn_hit], |
+ [undef, "headerCovTableEntry", $fn_found], |
+ [undef, "headerCovTableEntry$style", $rate]]) |
+ if ($type != $HDR_TESTDESC); |
+ } |
+ # Branch coverage |
+ if ($br_coverage) { |
+ $style = $rate_name[classify_rate($br_found, $br_hit, |
+ $br_med_limit, $br_hi_limit)]; |
+ $rate = rate($br_hit, $br_found, " %"); |
+ push(@row_right, [[undef, "headerItem", "Branches:"], |
+ [undef, "headerCovTableEntry", $br_hit], |
+ [undef, "headerCovTableEntry", $br_found], |
+ [undef, "headerCovTableEntry$style", $rate]]) |
+ if ($type != $HDR_TESTDESC); |
+ } |
+ |
+ # Print rows |
+ $num_rows = max(scalar(@row_left), scalar(@row_right)); |
+ for ($i = 0; $i < $num_rows; $i++) { |
+ my $left = $row_left[$i]; |
+ my $right = $row_right[$i]; |
+ |
+ if (!defined($left)) { |
+ $left = [[undef, undef, undef], [undef, undef, undef]]; |
+ } |
+ if (!defined($right)) { |
+ $right = []; |
} |
+ write_header_line(*HTML_HANDLE, @{$left}, |
+ [ $i == 0 ? "5%" : undef, undef, undef], |
+ @{$right}); |
} |
+ |
+ # Fourth line |
write_header_epilog(*HTML_HANDLE, $base_dir); |
} |
# |
-# split_filename(filename) |
-# |
-# Return (path, filename, extension) for a given FILENAME. |
-# |
- |
-sub split_filename($) |
-{ |
- if (!$_[0]) { return(); } |
- my @path_components = split('/', $_[0]); |
- my @file_components = split('\.', pop(@path_components)); |
- my $extension = pop(@file_components); |
- |
- return (join("/",@path_components), join(".",@file_components), |
- $extension); |
-} |
- |
-# |
# get_sorted_keys(hash_ref, sort_type) |
# |
@@ -3817,15 +4419,18 @@ sub get_sorted_keys($$) |
{ |
my ($hash, $type) = @_; |
- if ($type == 0) { |
+ if ($type == $SORT_FILE) { |
# Sort by name |
return sort(keys(%{$hash})); |
- } elsif ($type == 1) { |
+ } elsif ($type == $SORT_LINE) { |
# Sort by line coverage |
- return sort({$hash->{$a}[5] <=> $hash->{$b}[5]} keys(%{$hash})); |
- } elsif ($type == 2) { |
+ return sort({$hash->{$a}[7] <=> $hash->{$b}[7]} keys(%{$hash})); |
+ } elsif ($type == $SORT_FUNC) { |
# Sort by function coverage; |
- return sort({$hash->{$a}[6] <=> $hash->{$b}[6]} keys(%{$hash})); |
+ return sort({$hash->{$a}[8] <=> $hash->{$b}[8]} keys(%{$hash})); |
+ } elsif ($type == $SORT_BRANCH) { |
+ # Sort by br coverage; |
+ return sort({$hash->{$a}[9] <=> $hash->{$b}[9]} keys(%{$hash})); |
} |
} |
@@ -3858,7 +4463,7 @@ sub get_file_code($$$$) |
my $link; |
if ($sort_button) { |
- if ($type == 1) { |
+ if ($type == $HEAD_NO_DETAIL) { |
$link = "index.$html_ext"; |
} else { |
$link = "index-detail.$html_ext"; |
@@ -3875,12 +4480,12 @@ sub get_line_code($$$$$) |
my $result = $text; |
my $sort_link; |
- if ($type == 1) { |
+ if ($type == $HEAD_NO_DETAIL) { |
# Just text |
if ($sort_button) { |
$sort_link = "index-sort-l.$html_ext"; |
} |
- } elsif ($type == 2) { |
+ } elsif ($type == $HEAD_DETAIL_HIDDEN) { |
# Text + link to detail view |
$result .= ' ( <a class="detail" href="index-detail'. |
$fileview_sortname[$sort_type].'.'.$html_ext. |
@@ -3910,7 +4515,7 @@ sub get_func_code($$$$) |
my $link; |
if ($sort_button) { |
- if ($type == 1) { |
+ if ($type == $HEAD_NO_DETAIL) { |
$link = "index-sort-f.$html_ext"; |
} else { |
$link = "index-detail-sort-f.$html_ext"; |
@@ -3920,9 +4525,26 @@ sub get_func_code($$$$) |
return $result; |
} |
+sub get_br_code($$$$) |
+{ |
+ my ($type, $text, $sort_button, $base) = @_; |
+ my $result = $text; |
+ my $link; |
+ |
+ if ($sort_button) { |
+ if ($type == $HEAD_NO_DETAIL) { |
+ $link = "index-sort-b.$html_ext"; |
+ } else { |
+ $link = "index-detail-sort-b.$html_ext"; |
+ } |
+ } |
+ $result .= get_sort_code($link, "Sort by branch coverage", $base); |
+ return $result; |
+} |
+ |
# |
# write_file_table(filehandle, base_dir, overview, testhash, testfnchash, |
-# fileview, sort_type) |
+# testbrhash, fileview, sort_type) |
# |
# Write a complete file table. OVERVIEW is a reference to a hash containing |
# the following mapping: |
@@ -3940,70 +4562,107 @@ sub get_func_code($$$$) |
# otherwise. |
# |
-sub write_file_table(*$$$$$$) |
+sub write_file_table(*$$$$$$$) |
{ |
local *HTML_HANDLE = $_[0]; |
my $base_dir = $_[1]; |
my $overview = $_[2]; |
my $testhash = $_[3]; |
my $testfnchash = $_[4]; |
- my $fileview = $_[5]; |
- my $sort_type = $_[6]; |
+ my $testbrhash = $_[5]; |
+ my $fileview = $_[6]; |
+ my $sort_type = $_[7]; |
my $filename; |
my $bar_graph; |
my $hit; |
my $found; |
my $fn_found; |
my $fn_hit; |
+ my $br_found; |
+ my $br_hit; |
my $page_link; |
my $testname; |
my $testdata; |
my $testfncdata; |
- my $testcount; |
- my $testfnccount; |
+ my $testbrdata; |
my %affecting_tests; |
my $line_code = ""; |
my $func_code; |
+ my $br_code; |
my $file_code; |
+ my @head_columns; |
# Determine HTML code for column headings |
if (($base_dir ne "") && $show_details) |
{ |
my $detailed = keys(%{$testhash}); |
- $file_code = get_file_code($detailed ? 2 : 1, |
+ $file_code = get_file_code($detailed ? $HEAD_DETAIL_HIDDEN : |
+ $HEAD_NO_DETAIL, |
$fileview ? "Filename" : "Directory", |
- $sort && $sort_type != 0, $base_dir); |
- $line_code = get_line_code($detailed ? 3 : 2, $sort_type, |
+ $sort && $sort_type != $SORT_FILE, |
+ $base_dir); |
+ $line_code = get_line_code($detailed ? $HEAD_DETAIL_SHOWN : |
+ $HEAD_DETAIL_HIDDEN, |
+ $sort_type, |
"Line Coverage", |
- $sort && $sort_type != 1, $base_dir); |
- $func_code = get_func_code($detailed ? 2 : 1, "Functions", |
- $sort && $sort_type != 2, $base_dir); |
+ $sort && $sort_type != $SORT_LINE, |
+ $base_dir); |
+ $func_code = get_func_code($detailed ? $HEAD_DETAIL_HIDDEN : |
+ $HEAD_NO_DETAIL, |
+ "Functions", |
+ $sort && $sort_type != $SORT_FUNC, |
+ $base_dir); |
+ $br_code = get_br_code($detailed ? $HEAD_DETAIL_HIDDEN : |
+ $HEAD_NO_DETAIL, |
+ "Branches", |
+ $sort && $sort_type != $SORT_BRANCH, |
+ $base_dir); |
} else { |
- $file_code = get_file_code(1, |
+ $file_code = get_file_code($HEAD_NO_DETAIL, |
$fileview ? "Filename" : "Directory", |
- $sort && $sort_type != 0, $base_dir); |
- $line_code = get_line_code(1, $sort_type, "Line Coverage", |
- $sort && $sort_type != 1, $base_dir); |
- $func_code = get_func_code(1, "Functions", |
- $sort && $sort_type != 2, $base_dir); |
- } |
- |
- write_file_table_prolog(*HTML_HANDLE, $file_code, $line_code, |
- $func_code); |
+ $sort && $sort_type != $SORT_FILE, |
+ $base_dir); |
+ $line_code = get_line_code($HEAD_NO_DETAIL, $sort_type, "Line Coverage", |
+ $sort && $sort_type != $SORT_LINE, |
+ $base_dir); |
+ $func_code = get_func_code($HEAD_NO_DETAIL, "Functions", |
+ $sort && $sort_type != $SORT_FUNC, |
+ $base_dir); |
+ $br_code = get_br_code($HEAD_NO_DETAIL, "Branches", |
+ $sort && $sort_type != $SORT_BRANCH, |
+ $base_dir); |
+ } |
+ push(@head_columns, [ $line_code, 3 ]); |
+ push(@head_columns, [ $func_code, 2]) if ($func_coverage); |
+ push(@head_columns, [ $br_code, 2]) if ($br_coverage); |
+ |
+ write_file_table_prolog(*HTML_HANDLE, $file_code, @head_columns); |
foreach $filename (get_sorted_keys($overview, $sort_type)) |
{ |
- ($found, $hit, $fn_found, $fn_hit, $page_link) |
- = @{$overview->{$filename}}; |
- $bar_graph = get_bar_graph_code($base_dir, $found, $hit); |
+ my @columns; |
+ ($found, $hit, $fn_found, $fn_hit, $br_found, $br_hit, |
+ $page_link) = @{$overview->{$filename}}; |
+ |
+ # Line coverage |
+ push(@columns, [$found, $hit, $med_limit, $hi_limit, 1]); |
+ # Function coverage |
+ if ($func_coverage) { |
+ push(@columns, [$fn_found, $fn_hit, $fn_med_limit, |
+ $fn_hi_limit, 0]); |
+ } |
+ # Branch coverage |
+ if ($br_coverage) { |
+ push(@columns, [$br_found, $br_hit, $br_med_limit, |
+ $br_hi_limit, 0]); |
+ } |
+ write_file_table_entry(*HTML_HANDLE, $base_dir, $filename, |
+ $page_link, @columns); |
$testdata = $testhash->{$filename}; |
$testfncdata = $testfnchash->{$filename}; |
- |
- write_file_table_entry(*HTML_HANDLE, $filename, $bar_graph, |
- $found, $hit, $fn_found, $fn_hit, |
- $page_link); |
+ $testbrdata = $testbrhash->{$filename}; |
# Check whether we should write test specific coverage |
# as well |
@@ -4011,18 +4670,15 @@ sub write_file_table(*$$$$$$) |
# Filter out those tests that actually affect this file |
%affecting_tests = %{ get_affecting_tests($testdata, |
- $testfncdata) }; |
+ $testfncdata, $testbrdata) }; |
# Does any of the tests affect this file at all? |
if (!%affecting_tests) { next; } |
- # Write test details for this entry |
- write_file_table_detail_heading(*HTML_HANDLE, "Test name", |
- "Lines hit", "Functions hit"); |
- |
foreach $testname (keys(%affecting_tests)) |
{ |
- ($found, $hit, $fn_found, $fn_hit) = |
+ my @results; |
+ ($found, $hit, $fn_found, $fn_hit, $br_found, $br_hit) = |
split(",", $affecting_tests{$testname}); |
# Insert link to description of available |
@@ -4033,8 +4689,11 @@ sub write_file_table(*$$$$$$) |
"$testname</a>"; |
} |
+ push(@results, [$found, $hit]); |
+ push(@results, [$fn_found, $fn_hit]) if ($func_coverage); |
+ push(@results, [$br_found, $br_hit]) if ($br_coverage); |
write_file_table_detail_entry(*HTML_HANDLE, $testname, |
- $found, $hit, $fn_found, $fn_hit); |
+ @results); |
} |
} |
@@ -4095,39 +4754,224 @@ sub get_func_found_and_hit($) |
# |
-# get_affecting_tests(testdata, testfncdata) |
+# br_taken_to_num(taken) |
+# |
+# Convert a branch taken value .info format to number format. |
+# |
+ |
+sub br_taken_to_num($) |
+{ |
+ my ($taken) = @_; |
+ |
+ return 0 if ($taken eq '-'); |
+ return $taken + 1; |
+} |
+ |
+ |
+# |
+# br_num_to_taken(taken) |
+# |
+# Convert a branch taken value in number format to .info format. |
+# |
+ |
+sub br_num_to_taken($) |
+{ |
+ my ($taken) = @_; |
+ |
+ return '-' if ($taken == 0); |
+ return $taken - 1; |
+} |
+ |
+ |
+# |
+# br_taken_add(taken1, taken2) |
+# |
+# Return the result of taken1 + taken2 for 'branch taken' values. |
+# |
+ |
+sub br_taken_add($$) |
+{ |
+ my ($t1, $t2) = @_; |
+ |
+ return $t1 if (!defined($t2)); |
+ return $t2 if (!defined($t1)); |
+ return $t1 if ($t2 eq '-'); |
+ return $t2 if ($t1 eq '-'); |
+ return $t1 + $t2; |
+} |
+ |
+ |
+# |
+# br_taken_sub(taken1, taken2) |
+# |
+# Return the result of taken1 - taken2 for 'branch taken' values. Return 0 |
+# if the result would become negative. |
+# |
+ |
+sub br_taken_sub($$) |
+{ |
+ my ($t1, $t2) = @_; |
+ |
+ return $t1 if (!defined($t2)); |
+ return undef if (!defined($t1)); |
+ return $t1 if ($t1 eq '-'); |
+ return $t1 if ($t2 eq '-'); |
+ return 0 if $t2 > $t1; |
+ return $t1 - $t2; |
+} |
+ |
+ |
+# |
+# br_ivec_len(vector) |
+# |
+# Return the number of entries in the branch coverage vector. |
+# |
+ |
+sub br_ivec_len($) |
+{ |
+ my ($vec) = @_; |
+ |
+ return 0 if (!defined($vec)); |
+ return (length($vec) * 8 / $BR_VEC_WIDTH) / $BR_VEC_ENTRIES; |
+} |
+ |
+ |
+# |
+# br_ivec_get(vector, number) |
+# |
+# Return an entry from the branch coverage vector. |
+# |
+ |
+sub br_ivec_get($$) |
+{ |
+ my ($vec, $num) = @_; |
+ my $block; |
+ my $branch; |
+ my $taken; |
+ my $offset = $num * $BR_VEC_ENTRIES; |
+ |
+ # Retrieve data from vector |
+ $block = vec($vec, $offset + $BR_BLOCK, $BR_VEC_WIDTH); |
+ $branch = vec($vec, $offset + $BR_BRANCH, $BR_VEC_WIDTH); |
+ $taken = vec($vec, $offset + $BR_TAKEN, $BR_VEC_WIDTH); |
+ |
+ # Decode taken value from an integer |
+ $taken = br_num_to_taken($taken); |
+ |
+ return ($block, $branch, $taken); |
+} |
+ |
+ |
+# |
+# br_ivec_push(vector, block, branch, taken) |
+# |
+# Add an entry to the branch coverage vector. If an entry with the same |
+# branch ID already exists, add the corresponding taken values. |
+# |
+ |
+sub br_ivec_push($$$$) |
+{ |
+ my ($vec, $block, $branch, $taken) = @_; |
+ my $offset; |
+ my $num = br_ivec_len($vec); |
+ my $i; |
+ |
+ $vec = "" if (!defined($vec)); |
+ |
+ # 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); |
+ |
+ next if ($v_block != $block || $v_branch != $branch); |
+ |
+ # Add taken counts |
+ $taken = br_taken_add($taken, $v_taken); |
+ last; |
+ } |
+ |
+ $offset = $i * $BR_VEC_ENTRIES; |
+ $taken = br_taken_to_num($taken); |
+ |
+ # Add to vector |
+ vec($vec, $offset + $BR_BLOCK, $BR_VEC_WIDTH) = $block; |
+ vec($vec, $offset + $BR_BRANCH, $BR_VEC_WIDTH) = $branch; |
+ vec($vec, $offset + $BR_TAKEN, $BR_VEC_WIDTH) = $taken; |
+ |
+ return $vec; |
+} |
+ |
+ |
+# |
+# get_br_found_and_hit(sumbrcount) |
+# |
+# Return (br_found, br_hit) for sumbrcount |
+# |
+ |
+sub get_br_found_and_hit($) |
+{ |
+ my ($sumbrcount) = @_; |
+ my $line; |
+ my $br_found = 0; |
+ my $br_hit = 0; |
+ |
+ foreach $line (keys(%{$sumbrcount})) { |
+ my $brdata = $sumbrcount->{$line}; |
+ my $i; |
+ my $num = br_ivec_len($brdata); |
+ |
+ for ($i = 0; $i < $num; $i++) { |
+ my $taken; |
+ |
+ (undef, undef, $taken) = br_ivec_get($brdata, $i); |
+ |
+ $br_found++; |
+ $br_hit++ if ($taken ne "-" && $taken > 0); |
+ } |
+ } |
+ |
+ return ($br_found, $br_hit); |
+} |
+ |
+ |
+# |
+# get_affecting_tests(testdata, testfncdata, testbrdata) |
# |
# HASHREF contains a mapping filename -> (linenumber -> exec count). Return |
# a hash containing mapping filename -> "lines found, lines hit" for each |
# filename which has a nonzero hit count. |
# |
-sub get_affecting_tests($$) |
+sub get_affecting_tests($$$) |
{ |
- my $testdata = $_[0]; |
- my $testfncdata = $_[1]; |
+ my ($testdata, $testfncdata, $testbrdata) = @_; |
my $testname; |
my $testcount; |
my $testfnccount; |
+ my $testbrcount; |
my %result; |
my $found; |
my $hit; |
my $fn_found; |
my $fn_hit; |
+ my $br_found; |
+ my $br_hit; |
foreach $testname (keys(%{$testdata})) |
{ |
# Get (line number -> count) hash for this test case |
$testcount = $testdata->{$testname}; |
$testfnccount = $testfncdata->{$testname}; |
+ $testbrcount = $testbrdata->{$testname}; |
# Calculate sum |
($found, $hit) = get_found_and_hit($testcount); |
($fn_found, $fn_hit) = get_func_found_and_hit($testfnccount); |
+ ($br_found, $br_hit) = get_br_found_and_hit($testbrcount); |
if ($hit>0) |
{ |
- $result{$testname} = "$found,$hit,$fn_found,$fn_hit"; |
+ $result{$testname} = "$found,$hit,$fn_found,$fn_hit,". |
+ "$br_found,$br_hit"; |
} |
} |
@@ -4149,7 +4993,7 @@ sub get_hash_reverse($) |
# |
# write_source(filehandle, source_filename, count_data, checksum_data, |
-# converted_data, func_data) |
+# converted_data, func_data, sumbrcount) |
# |
# Write an HTML view of a source code file. Returns a list containing |
# data as needed by gen_png(). |
@@ -4157,7 +5001,7 @@ sub get_hash_reverse($) |
# Die on error. |
# |
-sub write_source($$$$$$) |
+sub write_source($$$$$$$) |
{ |
local *HTML_HANDLE = $_[0]; |
local *SOURCE_HANDLE; |
@@ -4168,24 +5012,51 @@ sub write_source($$$$$$) |
my $checkdata = $_[3]; |
my $converted = $_[4]; |
my $funcdata = $_[5]; |
+ 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"); |
- or open(SOURCE_HANDLE, "</dev/null"); |
+ 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 |
+ s/\015$//; |
+ |
# Source code matches coverage data? |
if (defined($checkdata->{$line_number}) && |
($checkdata->{$line_number} ne md5_base64($_))) |
@@ -4212,7 +5083,7 @@ sub write_source($$$$$$) |
write_source_line(HTML_HANDLE, $line_number, |
$_, $count_data{$line_number}, |
$converted->{$line_number}, |
- $add_anchor)); |
+ $sumbrcount->{$line_number}, $add_anchor)); |
} |
close(SOURCE_HANDLE); |
@@ -4271,7 +5142,8 @@ sub funcview_get_sorted($$$) |
# |
# write_function_table(filehandle, source_file, sumcount, funcdata, |
-# sumfnccount, testfncdata) |
+# sumfnccount, testfncdata, sumbrcount, testbrdata, |
+# base_name, base_dir, sort_type) |
# |
# Write an HTML table listing all functions in a source file, including |
# also function call counts and line coverages inside of each function. |
@@ -4279,7 +5151,7 @@ sub funcview_get_sorted($$$) |
# Die on error. |
# |
-sub write_function_table(*$$$$$$$$) |
+sub write_function_table(*$$$$$$$$$$) |
{ |
local *HTML_HANDLE = $_[0]; |
my $source = $_[1]; |
@@ -4287,9 +5159,11 @@ sub write_function_table(*$$$$$$$$) |
my $funcdata = $_[3]; |
my $sumfncdata = $_[4]; |
my $testfncdata = $_[5]; |
- my $name = $_[6]; |
- my $base = $_[7]; |
- my $type = $_[8]; |
+ my $sumbrcount = $_[6]; |
+ my $testbrdata = $_[7]; |
+ my $name = $_[8]; |
+ my $base = $_[9]; |
+ my $type = $_[10]; |
my $func; |
my $func_code; |
my $count_code; |
@@ -4310,11 +5184,23 @@ END_OF_HTML |
# Get a sorted table |
foreach $func (funcview_get_sorted($funcdata, $sumfncdata, $type)) { |
+ if (!defined($funcdata->{$func})) |
+ { |
+ next; |
+ } |
+ |
my $startline = $funcdata->{$func} - $func_offset; |
- my $name = escape_html($func); |
+ my $name = $func; |
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 |
+ $name = escape_html($name); |
if ($startline < 1) { |
$startline = 1; |
} |
@@ -4403,14 +5289,16 @@ sub subtract_counts($$) |
sub subtract_fnccounts($$) |
{ |
- my %data = %{$_[0]}; |
- my %base = %{$_[1]}; |
+ my %data; |
+ my %base; |
my $func; |
my $data_count; |
my $base_count; |
my $fn_hit = 0; |
my $fn_found = 0; |
+ %data = %{$_[0]} if (defined($_[0])); |
+ %base = %{$_[1]} if (defined($_[1])); |
foreach $func (keys(%data)) { |
$fn_found++; |
$data_count = $data{$func}; |
@@ -4453,18 +5341,24 @@ sub apply_baseline($$) |
my $data_funcdata; |
my $data_checkdata; |
my $data_testfncdata; |
+ my $data_testbrdata; |
my $data_count; |
my $data_testfnccount; |
+ my $data_testbrcount; |
my $base; |
my $base_checkdata; |
my $base_sumfnccount; |
+ my $base_sumbrcount; |
my $base_count; |
my $sumcount; |
my $sumfnccount; |
+ my $sumbrcount; |
my $found; |
my $hit; |
my $fn_found; |
my $fn_hit; |
+ my $br_found; |
+ my $br_hit; |
foreach $filename (keys(%data_hash)) |
{ |
@@ -4480,9 +5374,11 @@ sub apply_baseline($$) |
# Get set entries for data and baseline |
($data_testdata, undef, $data_funcdata, $data_checkdata, |
- $data_testfncdata) = get_info_entry($data); |
+ $data_testfncdata, undef, $data_testbrdata) = |
+ get_info_entry($data); |
(undef, $base_count, undef, $base_checkdata, undef, |
- $base_sumfnccount) = get_info_entry($base); |
+ $base_sumfnccount, undef, $base_sumbrcount) = |
+ get_info_entry($base); |
# Check for compatible checksums |
merge_checksums($data_checkdata, $base_checkdata, $filename); |
@@ -4490,6 +5386,7 @@ sub apply_baseline($$) |
# sumcount has to be calculated anew |
$sumcount = {}; |
$sumfnccount = {}; |
+ $sumbrcount = {}; |
# For each test case, subtract test specific counts |
foreach $testname (keys(%{$data_testdata})) |
@@ -4497,12 +5394,17 @@ sub apply_baseline($$) |
# Get counts of both data and baseline |
$data_count = $data_testdata->{$testname}; |
$data_testfnccount = $data_testfncdata->{$testname}; |
+ $data_testbrcount = $data_testbrdata->{$testname}; |
($data_count, undef, $hit) = |
subtract_counts($data_count, $base_count); |
($data_testfnccount) = |
subtract_fnccounts($data_testfnccount, |
$base_sumfnccount); |
+ ($data_testbrcount) = |
+ combine_brcount($data_testbrcount, |
+ $base_sumbrcount, $BR_SUB); |
+ |
# Check whether this test case did hit any line at all |
if ($hit > 0) |
@@ -4511,6 +5413,8 @@ sub apply_baseline($$) |
$data_testdata->{$testname} = $data_count; |
$data_testfncdata->{$testname} = |
$data_testfnccount; |
+ $data_testbrdata->{$testname} = |
+ $data_testbrcount; |
} |
else |
{ |
@@ -4518,19 +5422,24 @@ sub apply_baseline($$) |
# file |
delete($data_testdata->{$testname}); |
delete($data_testfncdata->{$testname}); |
+ delete($data_testbrdata->{$testname}); |
} |
# Add counts to sum of counts |
($sumcount, $found, $hit) = |
add_counts($sumcount, $data_count); |
($sumfnccount, $fn_found, $fn_hit) = |
- add_fnccounts($sumfnccount, $data_testfnccount); |
+ add_fnccount($sumfnccount, $data_testfnccount); |
+ ($sumbrcount, $br_found, $br_hit) = |
+ combine_brcount($sumbrcount, $data_testbrcount, |
+ $BR_ADD); |
} |
# Write back resulting entry |
set_info_entry($data, $data_testdata, $sumcount, $data_funcdata, |
$data_checkdata, $data_testfncdata, $sumfnccount, |
- $found, $hit, $fn_found, $fn_hit); |
+ $data_testbrdata, $sumbrcount, $found, $hit, |
+ $fn_found, $fn_hit, $br_found, $br_hit); |
$data_hash{$filename} = $data; |
} |
@@ -4627,12 +5536,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 = $?; |
@@ -4642,8 +5551,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; |
} |
@@ -4664,7 +5573,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; |
@@ -4703,8 +5612,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($) |
@@ -4713,8 +5622,9 @@ sub apply_config($) |
foreach (keys(%{$ref})) |
{ |
- if (defined($config->{$_})) |
- { |
+ if (defined($opt_rc{$_})) { |
+ ${$ref->{$_}} = $opt_rc{$_}; |
+ } elsif (defined($config->{$_})) { |
${$ref->{$_}} = $config->{$_}; |
} |
} |
@@ -4737,7 +5647,7 @@ sub get_html_prolog($) |
{ |
local *HANDLE; |
- open(HANDLE, "<".$filename) |
+ open(HANDLE, "<", $filename) |
or die("ERROR: cannot open html prolog $filename!\n"); |
while (<HANDLE>) |
{ |
@@ -4753,7 +5663,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> |
@@ -4783,7 +5693,7 @@ sub get_html_epilog($) |
{ |
local *HANDLE; |
- open(HANDLE, "<".$filename) |
+ open(HANDLE, "<", $filename) |
or die("ERROR: cannot open html epilog $filename!\n"); |
while (<HANDLE>) |
{ |
@@ -4818,3 +5728,72 @@ 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; |
+ } |
+} |
+ |
+# |
+# 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); |
+} |