diff --git a/utils/lcov/genhtml b/utils/lcov/genhtml index 9502a27c1..d74063a4f 100755 --- a/utils/lcov/genhtml +++ b/utils/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 @@ -48,6 +48,20 @@ # 2003-04-30 / Peter Oberparleiter: made info write to STDERR, not STDOUT # 2003-07-10 / Peter Oberparleiter: added line checksum support # 2004-08-09 / Peter Oberparleiter: added configuration file support +# 2005-03-04 / Cal Pierog: added legend to HTML output, fixed coloring of +# "good coverage" background +# 2006-03-18 / Marcus Boerger: added --custom-intro, --custom-outro and +# overwrite --no-prefix if --prefix is present +# 2006-03-20 / Peter Oberparleiter: changes to custom_* function (rename +# to html_prolog/_epilog, minor modifications to implementation), +# changed prefix/noprefix handling to be consistent with current +# logic +# 2006-03-20 / Peter Oberparleiter: added --html-extension option +# 2008-07-14 / Tom Zoerner: added --function-coverage command line option; +# added function table to source file page +# 2008-08-13 / Peter Oberparleiter: modified function coverage +# implementation (now enabled per default), +# introduced sorting option (enabled per default) # use strict; @@ -57,16 +71,27 @@ use Digest::MD5 qw(md5_base64); # Global constants -our $title = "LTP GCOV extension - code coverage report"; -our $lcov_version = "LTP GCOV extension version 1.4"; +our $title = "LCOV - code coverage report"; +our $lcov_version = 'LCOV version 1.9'; our $lcov_url = "http://ltp.sourceforge.net/coverage/lcov.php"; +our $tool_name = basename($0); # Specify coverage rate limits (in %) for classifying file entries # HI: $hi_limit <= rate <= 100 graph color: green # MED: $med_limit <= rate < $hi_limit graph color: orange # LO: 0 <= rate < $med_limit graph color: red -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; +our $fn_med_limit; + +# For branch coverage +our $br_hi_limit; +our $br_med_limit; # Width of overview image our $overview_width = 80; @@ -81,31 +106,78 @@ our $nav_resolution = 4; # line in the window. This number specifies that offset in lines. our $nav_offset = 10; -our $overview_title = "directory"; +# Clicking on a function name should show the source code at a position a +# few lines before the first line of code of that function. This number +# specifies that offset in lines. +our $func_offset = 2; + +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(*); sub gen_html(); +sub html_create($$); sub process_dir($); 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($$$); @@ -113,6 +185,19 @@ sub apply_prefix($$); sub system_no_output($@); 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 @@ -120,32 +205,33 @@ sub escape_html($); sub get_bar_graph_code($$$); sub write_png_files(); +sub write_htaccess_file(); sub write_css_file(); -sub write_description_file($$$); +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(*$$$); @@ -173,12 +259,31 @@ our $help; # Help option flag our $version; # Version option flag our $show_details; # If set, generate detailed directory view our $no_prefix; # If set, do not remove filename prefix +our $func_coverage = 1; # If set, generate function coverage statistics +our $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 our $keep_descriptions; # If set, do not remove unused test case descriptions our $no_sourceview; # If set, do not create a source code view for each file our $highlight; # If set, highlight lines covered by converted data only +our $legend; # If set, include legend in output our $tab_size = 8; # Number of spaces to use in place of tab our $config; # Configuration file contents +our $html_prolog_file; # Custom HTML prolog file (up to and including
) +our $html_epilog_file; # Custom HTML epilog file (from onwards) +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", "-sort-b"); +our @funcview_sortlist; +our @rate_name = ("Lo", "Med", "Hi"); +our @rate_png = ("ruby.png", "amber.png", "emerald.png"); our $cwd = `pwd`; # Current working directory chomp($cwd); @@ -189,6 +294,12 @@ our $tool_dir = dirname($0); # Directory where genhtml tool is installed # Code entry point # +$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 =~ /^\/(.*)$/)) { @@ -196,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"); } @@ -212,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, @@ -219,30 +331,75 @@ if ($config) "genhtml_no_prefix" => \$no_prefix, "genhtml_no_source" => \$no_sourceview, "genhtml_num_spaces" => \$tab_size, - "genhtml_highlight" => \$highlight}); + "genhtml_highlight" => \$highlight, + "genhtml_legend" => \$legend, + "genhtml_html_prolog" => \$html_prolog_file, + "genhtml_html_epilog" => \$html_epilog_file, + "genhtml_html_extension" => \$html_ext, + "genhtml_html_gzip" => \$html_gzip, + "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, - "quiet" => \$quiet, - "help|h" => \$help, - "version" => \$version + "legend" => \$legend, + "quiet|q" => \$quiet, + "help|h|?" => \$help, + "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_usage(*STDERR); + print(STDERR "Use $tool_name --help to get usage information\n"); exit(1); +} else { + # Merge options + if ($no_func_coverage) { + $func_coverage = 0; + } + if ($no_br_coverage) { + $br_coverage = 0; + } + + # Merge sort options + if ($no_sort) { + $sort = 0; + } } @info_filenames = @ARGV; @@ -257,16 +414,15 @@ if ($help) # Check for version option if ($version) { - print($lcov_version."\n"); + print("$tool_name: $lcov_version\n"); exit(0); } # Check for info filename if (!@info_filenames) { - print(STDERR "No filename specified\n"); - print_usage(*STDERR); - exit(1); + die("No filename specified\n". + "Use $tool_name --help to get usage information\n"); } # Generate a title if none is specified @@ -302,20 +458,51 @@ if ($tab_size < 1) exit(1); } +# Get HTML prolog and epilog +$html_prolog = get_html_prolog($html_prolog_file); +$html_epilog = get_html_epilog($html_epilog_file); + # Issue a warning if --no-sourceview is enabled together with --frames -if ($no_sourceview && $frames) +if ($no_sourceview && defined($frames)) { warn("WARNING: option --frames disabled because --no-sourceview ". "was specified!\n"); $frames = undef; } +# Issue a warning if --no-prefix is enabled together with --prefix +if ($no_prefix && defined($dir_prefix)) +{ + warn("WARNING: option --prefix disabled because --no-prefix was ". + "specified!\n"); + $dir_prefix = undef; +} + +@fileview_sortlist = ($SORT_FILE); +@funcview_sortlist = ($SORT_FILE); + +if ($sort) { + 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) { # Include genpng code needed for overview image generation 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) { @@ -323,12 +510,10 @@ if ($output_directory) if (! -e _) { - system("mkdir", "-p", $output_directory) - and die("ERROR: cannot create directory $_!\n"); + create_sub_dir($output_directory); } } - # Do something gen_html(); @@ -345,37 +530,113 @@ exit(0); sub print_usage(*) { local *HANDLE = $_[0]; - my $executable_name = basename($0); print(HANDLE <| - | - | - | |||
| $_[1] | -$_[2] | -||||
| $file_heading | +END_OF_HTML + # Heading row + foreach $col (@columns) { + my ($heading, $cols) = @{$col}; + my $colspan = ""; + + $colspan = " colspan=$cols" if ($cols > 1); + write_html($handle, <$_[1] | +$file_code | +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, <$rate_string | -$_[4] / $_[3] lines | -$hit / $found | +END_OF_HTML + } + # End of row + write_html($handle, <$_[1] | -$_[2] | - - -END_OF_HTML - ; - - # ************************************************************* -} - - -# -# write_file_table_detail_entry(filehandle, test_name, cover_found, cover_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 $name = $_[1]; - - if ($_[2]>0) - { - $rate = sprintf("%.1f", $_[3]*100/$_[2])." %"; - } - else - { - $rate = "undefined"; - } + my ($handle, $test, @entries) = @_; + my $entry; - if ($name =~ /^(.*),diff$/) - { - $name = $1." (converted)"; + if ($test eq "") { + $test = "<unnamed>"; + } elsif ($test =~ /^(.*),diff$/) { + $test = $1." (converted)"; } - - if ($name eq "") - { - $name = "<unnamed>"; - } - - # ************************************************************* - - write_html($_[0], <$name | +$test | +END_OF_HTML + # Test data + foreach $entry (@entries) { + my ($found, $hit) = @{$entry}; + my $rate = "-"; + + if ($found > 0) { + $rate = sprintf("%.1f %%", $hit * 100 / $found); + } + write_html($handle, <$_[3] / $_[2] lines | +$hit / $found | +END_OF_HTML + } + + write_html($handle, <
+ | |||||