diff options
Diffstat (limited to '3rdParty/LCov/genhtml')
| -rwxr-xr-x | 3rdParty/LCov/genhtml | 2151 | 
1 files changed, 1490 insertions, 661 deletions
| diff --git a/3rdParty/LCov/genhtml b/3rdParty/LCov/genhtml index 497363b..d74063a 100755 --- a/3rdParty/LCov/genhtml +++ b/3rdParty/LCov/genhtml @@ -1,6 +1,6 @@  #!/usr/bin/perl -w  # -#   Copyright (c) International Business Machines  Corp., 2002 +#   Copyright (c) International Business Machines  Corp., 2002,2010  #  #   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.9';  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,49 @@ 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;  # Data related prototypes  sub print_usage(*); @@ -118,21 +164,20 @@ sub process_file($$$);  sub info(@);  sub read_info_file($);  sub get_info_entry($); -sub set_info_entry($$$$$$$;$$$$); +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 +187,17 @@ 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($);  # HTML related prototypes @@ -151,32 +207,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(*$$$); @@ -206,6 +261,8 @@ 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 $no_func_coverage;	# Disable func_coverage +our $br_coverage = 1;	# 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,8 +278,9 @@ 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 @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"); @@ -239,6 +297,9 @@ 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 =~ /^\/(.*)$/))  { @@ -246,7 +307,7 @@ if (! ($tool_dir =~ /^\/(.*)$/))  }  # Read configuration file if available -if (-r $ENV{"HOME"}."/.lcovrc") +if (defined($ENV{"HOME"}) && (-r $ENV{"HOME"}."/.lcovrc"))  {  	$config = read_config($ENV{"HOME"}."/.lcovrc");  } @@ -262,6 +323,7 @@ if ($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 +340,49 @@ 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,  		});  } +# Copy limit 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)); +  # 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,  		))  {  	print(STDERR "Use $tool_name --help to get usage information\n"); @@ -317,6 +392,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) { @@ -400,16 +478,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 +494,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 +510,7 @@ if ($output_directory)  	if (! -e _)  	{ -		system("mkdir", "-p", $output_directory) -			and die("ERROR: cannot create directory $_!\n"); +		create_sub_dir($output_directory);  	}  } @@ -467,6 +551,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 +566,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 +594,50 @@ 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 sprintf("%.1f%% (%d of %d %s)", $hit * 100 / $found, $hit, +		       $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 +657,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; @@ -625,7 +759,8 @@ 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);  		# Remove prefix if applicable @@ -646,13 +781,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 +800,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 +810,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);  }  # @@ -727,11 +843,12 @@ sub html_create($$)  	}  } -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"); @@ -741,9 +858,9 @@ sub write_dir_page($$$$$$$$$$$$$$)  	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 +882,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; @@ -804,8 +927,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 +943,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 +969,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 +978,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 +1046,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 +1066,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 +1097,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 +1131,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 +1144,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 +1177,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 +1198,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 +1238,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 +1249,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"); @@ -1146,7 +1301,7 @@ sub read_info_file($)  		# Switch statement  		foreach ($line)  		{ -			/^TN:([^,]*)/ && do +			/^TN:([^,]*)(,diff)?/ && do  			{  				# Test name information found  				$testname = defined($1) ? $1 : ""; @@ -1154,6 +1309,7 @@ sub read_info_file($)  				{  					$changed_testname = 1;  				} +				$testname .= $2 if (defined($2));  				last;  			}; @@ -1165,18 +1321,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;  			}; @@ -1249,6 +1408,27 @@ sub read_info_file($)  				}  				last;  			}; + +			/^BRDA:(\d+),(\d+),(\d+),(\d+|-)/ && do { +				# Branch coverage data found +				my ($line, $block, $branch, $taken) = +				   ($1, $2, $3, $4); + +				$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 +1441,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 +1468,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 +1508,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 +1553,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 +1588,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 +1704,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 +1738,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 +1794,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 +1971,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 +1981,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 +1990,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 +2019,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 +2066,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);  } @@ -1986,14 +2366,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 +2386,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"); @@ -2328,17 +2713,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 +2793,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 +2805,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 +2817,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 +2829,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 +2841,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 +2853,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 +2883,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 +2893,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 +2903,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 +2942,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 +2953,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 +2999,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,13 +3017,96 @@ 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 +	{ +	  background-color: #CAD7FE; +	} + +	/* Source code view: format for branches which were executed +	 * but not taken */ +	span.branchNoCov +	{ +	  background-color: #FF6230; +	} + +	/* Source code view: format for branches which were not executed */ +	span.branchNoExec +	{ +	  background-color: #FF6230; +	} + +	/* Source code view: format for the source code heading line */ +	pre.sourceHeading +	{ +	  white-space: pre; +	  font-family: monospace; +	  font-weight: bold; +	  margin: 0px; +	} + +	/* 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%; +	} + +	/* 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; -	  background-color: #B5F7AF; +	  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  	; @@ -2853,104 +3275,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; - -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; +	write_html($handle, "          <tr>\n"); +	foreach $entry (@content) { +		my ($width, $class, $text, $colspan) = @{$entry}; -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 +3323,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,84 +3339,82 @@ 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;  	# Add link to source if provided  	if (defined($page_link) && $page_link ne "") { @@ -3066,158 +3423,88 @@ sub write_file_table_entry(*$$$$$$$)  		$file_code = $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 = sprintf("%.1f %%", $hit * 100 / $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 ($test eq "") { +		$test = "<span style=\"font-style:italic\"><unnamed></span>"; +	} elsif ($test =~ /^(.*),diff$/) { +		$test = $1." (converted)";  	} - -	if ($name eq "") -	{ -		$name = "<span style=\"font-style:italic\"><unnamed></span>"; -	} - -	# ************************************************************* - -	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 = "-"; + +		if ($found > 0) { +			$rate = sprintf("%.1f %%", $hit * 100 / $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 +3609,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 +3628,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 +3645,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 +3656,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 +3909,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 +3979,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 @@ -3601,28 +4120,6 @@ END_OF_HTML  } -# rate_to_col(found, hit) -# -# Return Lo, Med or Hi, depending on the coverage rate. -# - -sub rate_to_col($$) -{ -	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. @@ -3633,20 +4130,16 @@ sub format_rate($$)  	return $_[0] == 0 ? "-" : sprintf("%.1f", $_[1] * 100 / $_[0])." %";  } -sub get_legend_code($$$) + +sub max($$)  { -	my ($text, $med, $hi) = @_; -	my $result; +	my ($a, $b) = @_; -	$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; +	return $a if ($a > $b); +	return $b;  } +  #  # write_header(filehandle, type, trunc_file_name, rel_file_name, lines_found,  # lines_hit, funcs_found, funcs_hit, sort_type) @@ -3656,7 +4149,7 @@ EOF  # header, test case description header, function view header)  # -sub write_header(*$$$$$$$$) +sub write_header(*$$$$$$$$$$)  {  	local *HTML_HANDLE = $_[0];  	my $type = $_[1]; @@ -3666,29 +4159,37 @@ 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;  	$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";  	} -	elsif ($type == 2 || $type == 4) +	elsif ($type == $HDR_SOURCE || $type == $HDR_FUNC)  	{  		# File view  		my $dir_name = dirname($rel_filename); @@ -3713,14 +4214,16 @@ sub write_header(*$$$$$$$$)  		# 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 +4235,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 = format_rate($lines_found, $lines_hit); +	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 = format_rate($fn_found, $fn_hit); +		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 = format_rate($br_found, $br_hit); +		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 +4362,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 +4406,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 +4423,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 +4458,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 +4468,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 +4505,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 +4613,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 +4632,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 +4697,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 +4936,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 +4944,7 @@ sub get_hash_reverse($)  # Die on error.  # -sub write_source($$$$$$) +sub write_source($$$$$$$)  {  	local *HTML_HANDLE = $_[0];  	local *SOURCE_HANDLE; @@ -4168,6 +4955,7 @@ sub write_source($$$$$$)  	my $checkdata = $_[3];  	my $converted = $_[4];  	my $funcdata  = $_[5]; +	my $sumbrcount = $_[6];  	my $datafunc = get_hash_reverse($funcdata);  	my $add_anchor; @@ -4185,6 +4973,9 @@ sub write_source($$$$$$)  	{  		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($_))) @@ -4211,7 +5002,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); @@ -4270,7 +5061,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. @@ -4278,7 +5070,7 @@ sub funcview_get_sorted($$$)  # Die on error.  # -sub write_function_table(*$$$$$$$$) +sub write_function_table(*$$$$$$$$$$)  {  	local *HTML_HANDLE = $_[0];  	my $source = $_[1]; @@ -4286,9 +5078,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; @@ -4309,11 +5103,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;  		} @@ -4402,14 +5208,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}; @@ -4452,18 +5260,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))  	{ @@ -4479,9 +5293,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); @@ -4489,6 +5305,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})) @@ -4496,12 +5313,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) @@ -4510,6 +5332,8 @@ sub apply_baseline($$)  				$data_testdata->{$testname} = $data_count;  				$data_testfncdata->{$testname} =  					$data_testfnccount; +				$data_testbrdata->{$testname} = +					$data_testbrcount;  			}  			else  			{ @@ -4517,19 +5341,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;  	} | 
 Swift
 Swift