Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(343)

Side by Side Diff: third_party/lcov/bin/genhtml

Issue 57083: lcov-1.7 into third_party for code coverage on POSIX systems.... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « third_party/lcov/bin/gendesc ('k') | third_party/lcov/bin/geninfo » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Property Changes:
Name: svn:executable
+ *
OLDNEW
(Empty)
1 #!/usr/bin/perl -w
2 #
3 # Copyright (c) International Business Machines Corp., 2002
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or (at
8 # your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 # General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 #
19 #
20 # genhtml
21 #
22 # This script generates HTML output from .info files as created by the
23 # geninfo script. Call it with --help and refer to the genhtml man page
24 # to get information on usage and available options.
25 #
26 #
27 # History:
28 # 2002-08-23 created by Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com>
29 # IBM Lab Boeblingen
30 # based on code by Manoj Iyer <manjo@mail.utexas.edu> and
31 # Megan Bock <mbock@us.ibm.com>
32 # IBM Austin
33 # 2002-08-27 / Peter Oberparleiter: implemented frame view
34 # 2002-08-29 / Peter Oberparleiter: implemented test description filtering
35 # so that by default only descriptions for test cases which
36 # actually hit some source lines are kept
37 # 2002-09-05 / Peter Oberparleiter: implemented --no-sourceview
38 # 2002-09-05 / Mike Kobler: One of my source file paths includes a "+" in
39 # the directory name. I found that genhtml.pl died when it
40 # encountered it. I was able to fix the problem by modifying
41 # the string with the escape character before parsing it.
42 # 2002-10-26 / Peter Oberparleiter: implemented --num-spaces
43 # 2003-04-07 / Peter Oberparleiter: fixed bug which resulted in an error
44 # when trying to combine .info files containing data without
45 # a test name
46 # 2003-04-10 / Peter Oberparleiter: extended fix by Mike to also cover
47 # other special characters
48 # 2003-04-30 / Peter Oberparleiter: made info write to STDERR, not STDOUT
49 # 2003-07-10 / Peter Oberparleiter: added line checksum support
50 # 2004-08-09 / Peter Oberparleiter: added configuration file support
51 # 2005-03-04 / Cal Pierog: added legend to HTML output, fixed coloring of
52 # "good coverage" background
53 # 2006-03-18 / Marcus Boerger: added --custom-intro, --custom-outro and
54 # overwrite --no-prefix if --prefix is present
55 # 2006-03-20 / Peter Oberparleiter: changes to custom_* function (rename
56 # to html_prolog/_epilog, minor modifications to implementation),
57 # changed prefix/noprefix handling to be consistent with current
58 # logic
59 # 2006-03-20 / Peter Oberparleiter: added --html-extension option
60 # 2008-07-14 / Tom Zoerner: added --function-coverage command line option;
61 # added function table to source file page
62 # 2008-08-13 / Peter Oberparleiter: modified function coverage
63 # implementation (now enabled per default),
64 # introduced sorting option (enabled per default)
65 #
66
67 use strict;
68 use File::Basename;
69 use Getopt::Long;
70 use Digest::MD5 qw(md5_base64);
71
72
73 # Global constants
74 our $title = "LCOV - code coverage report";
75 our $lcov_version = "LCOV version 1.7";
76 our $lcov_url = "http://ltp.sourceforge.net/coverage/lcov.php";
77 our $tool_name = basename($0);
78
79 # Specify coverage rate limits (in %) for classifying file entries
80 # HI: $hi_limit <= rate <= 100 graph color: green
81 # MED: $med_limit <= rate < $hi_limit graph color: orange
82 # LO: 0 <= rate < $med_limit graph color: red
83
84 # For line coverage
85 our $hi_limit = 50;
86 our $med_limit = 15;
87
88 # For function coverage
89 our $fn_hi_limit = 90;
90 our $fn_med_limit = 75;
91
92 # Width of overview image
93 our $overview_width = 80;
94
95 # Resolution of overview navigation: this number specifies the maximum
96 # difference in lines between the position a user selected from the overview
97 # and the position the source code window is scrolled to.
98 our $nav_resolution = 4;
99
100 # Clicking a line in the overview image should show the source code view at
101 # a position a bit further up so that the requested line is not the first
102 # line in the window. This number specifies that offset in lines.
103 our $nav_offset = 10;
104
105 # Clicking on a function name should show the source code at a position a
106 # few lines before the first line of code of that function. This number
107 # specifies that offset in lines.
108 our $func_offset = 2;
109
110 our $overview_title = "directory";
111
112 # Data related prototypes
113 sub print_usage(*);
114 sub gen_html();
115 sub html_create($$);
116 sub process_dir($);
117 sub process_file($$$);
118 sub info(@);
119 sub read_info_file($);
120 sub get_info_entry($);
121 sub set_info_entry($$$$$$$;$$$$);
122 sub get_prefix(@);
123 sub shorten_prefix($);
124 sub get_dir_list(@);
125 sub get_relative_base_path($);
126 sub read_testfile($);
127 sub get_date_string();
128 sub split_filename($);
129 sub create_sub_dir($);
130 sub subtract_counts($$);
131 sub add_counts($$);
132 sub apply_baseline($$);
133 sub remove_unused_descriptions();
134 sub get_found_and_hit($);
135 sub get_affecting_tests($$);
136 sub combine_info_files($$);
137 sub merge_checksums($$$);
138 sub combine_info_entries($$$);
139 sub apply_prefix($$);
140 sub system_no_output($@);
141 sub read_config($);
142 sub apply_config($);
143 sub get_html_prolog($);
144 sub get_html_epilog($);
145
146
147 # HTML related prototypes
148 sub escape_html($);
149 sub get_bar_graph_code($$$);
150
151 sub write_png_files();
152 sub write_htaccess_file();
153 sub write_css_file();
154 sub write_description_file($$$$$);
155 sub write_function_rable(*$$$);
156
157 sub write_html(*$);
158 sub write_html_prolog(*$$);
159 sub write_html_epilog(*$;$);
160
161 sub write_header(*$$$$$$$$);
162 sub write_header_prolog(*$);
163 sub write_header_line(*$@);
164 sub write_header_epilog(*$);
165
166 sub write_file_table(*$$$$$$);
167 sub write_file_table_prolog(*$$$);
168 sub write_file_table_entry(*$$$$$$$);
169 sub write_file_table_detail_heading(*$$$);
170 sub write_file_table_detail_entry(*$$$$$);
171 sub write_file_table_epilog(*);
172
173 sub write_test_table_prolog(*$);
174 sub write_test_table_entry(*$$);
175 sub write_test_table_epilog(*);
176
177 sub write_source($$$$$$);
178 sub write_source_prolog(*);
179 sub write_source_line(*$$$$$);
180 sub write_source_epilog(*);
181
182 sub write_frameset(*$$$);
183 sub write_overview_line(*$$$);
184 sub write_overview(*$$$$);
185
186 # External prototype (defined in genpng)
187 sub gen_png($$$@);
188
189
190 # Global variables & initialization
191 our %info_data; # Hash containing all data from .info file
192 our $dir_prefix; # Prefix to remove from all sub directories
193 our %test_description; # Hash containing test descriptions if available
194 our $date = get_date_string();
195
196 our @info_filenames; # List of .info files to use as data source
197 our $test_title; # Title for output as written to each page header
198 our $output_directory; # Name of directory in which to store output
199 our $base_filename; # Optional name of file containing baseline data
200 our $desc_filename; # Name of file containing test descriptions
201 our $css_filename; # Optional name of external stylesheet file to use
202 our $quiet; # If set, suppress information messages
203 our $help; # Help option flag
204 our $version; # Version option flag
205 our $show_details; # If set, generate detailed directory view
206 our $no_prefix; # If set, do not remove filename prefix
207 our $func_coverage = 1; # If set, generate function coverage statistics
208 our $no_func_coverage; # Disable func_coverage
209 our $sort = 1; # If set, provide directory listings with sorted entries
210 our $no_sort; # Disable sort
211 our $frames; # If set, use frames for source code view
212 our $keep_descriptions; # If set, do not remove unused test case descriptions
213 our $no_sourceview; # If set, do not create a source code view for each file
214 our $highlight; # If set, highlight lines covered by converted data only
215 our $legend; # If set, include legend in output
216 our $tab_size = 8; # Number of spaces to use in place of tab
217 our $config; # Configuration file contents
218 our $html_prolog_file; # Custom HTML prolog file (up to and including <body>)
219 our $html_epilog_file; # Custom HTML epilog file (from </body> onwards)
220 our $html_prolog; # Actual HTML prolog
221 our $html_epilog; # Actual HTML epilog
222 our $html_ext = "html"; # Extension for generated HTML files
223 our $html_gzip = 0; # Compress with gzip
224 our @fileview_sortlist;
225 our @fileview_sortname = ("", "-sort-l", "-sort-f");
226 our @funcview_sortlist;
227 our @rate_name = ("Lo", "Med", "Hi");
228 our @rate_png = ("ruby.png", "amber.png", "emerald.png");
229
230 our $cwd = `pwd`; # Current working directory
231 chomp($cwd);
232 our $tool_dir = dirname($0); # Directory where genhtml tool is installed
233
234
235 #
236 # Code entry point
237 #
238
239 $SIG{__WARN__} = \&warn_handler;
240 $SIG{__DIE__} = \&die_handler;
241
242 # Add current working directory if $tool_dir is not already an absolute path
243 if (! ($tool_dir =~ /^\/(.*)$/))
244 {
245 $tool_dir = "$cwd/$tool_dir";
246 }
247
248 # Read configuration file if available
249 if (-r $ENV{"HOME"}."/.lcovrc")
250 {
251 $config = read_config($ENV{"HOME"}."/.lcovrc");
252 }
253 elsif (-r "/etc/lcovrc")
254 {
255 $config = read_config("/etc/lcovrc");
256 }
257
258 if ($config)
259 {
260 # Copy configuration file values to variables
261 apply_config({
262 "genhtml_css_file" => \$css_filename,
263 "genhtml_hi_limit" => \$hi_limit,
264 "genhtml_med_limit" => \$med_limit,
265 "genhtml_overview_width" => \$overview_width,
266 "genhtml_nav_resolution" => \$nav_resolution,
267 "genhtml_nav_offset" => \$nav_offset,
268 "genhtml_keep_descriptions" => \$keep_descriptions,
269 "genhtml_no_prefix" => \$no_prefix,
270 "genhtml_no_source" => \$no_sourceview,
271 "genhtml_num_spaces" => \$tab_size,
272 "genhtml_highlight" => \$highlight,
273 "genhtml_legend" => \$legend,
274 "genhtml_html_prolog" => \$html_prolog_file,
275 "genhtml_html_epilog" => \$html_epilog_file,
276 "genhtml_html_extension" => \$html_ext,
277 "genhtml_html_gzip" => \$html_gzip,
278 "genhtml_function_hi_limit" => \$fn_hi_limit,
279 "genhtml_function_med_limit" => \$fn_med_limit,
280 "genhtml_function_coverage" => \$func_coverage,
281 "genhtml_sort" => \$sort,
282 });
283 }
284
285 # Parse command line options
286 if (!GetOptions("output-directory=s" => \$output_directory,
287 "title=s" => \$test_title,
288 "description-file=s" => \$desc_filename,
289 "keep-descriptions" => \$keep_descriptions,
290 "css-file=s" => \$css_filename,
291 "baseline-file=s" => \$base_filename,
292 "prefix=s" => \$dir_prefix,
293 "num-spaces=i" => \$tab_size,
294 "no-prefix" => \$no_prefix,
295 "no-sourceview" => \$no_sourceview,
296 "show-details" => \$show_details,
297 "frames" => \$frames,
298 "highlight" => \$highlight,
299 "legend" => \$legend,
300 "quiet" => \$quiet,
301 "help|h|?" => \$help,
302 "version" => \$version,
303 "html-prolog=s" => \$html_prolog_file,
304 "html-epilog=s" => \$html_epilog_file,
305 "html-extension=s" => \$html_ext,
306 "html-gzip" => \$html_gzip,
307 "function-coverage" => \$func_coverage,
308 "no-function-coverage" => \$no_func_coverage,
309 "sort" => \$sort,
310 "no-sort" => \$no_sort,
311 ))
312 {
313 print(STDERR "Use $tool_name --help to get usage information\n");
314 exit(1);
315 } else {
316 # Merge options
317 if ($no_func_coverage) {
318 $func_coverage = 0;
319 }
320
321 # Merge sort options
322 if ($no_sort) {
323 $sort = 0;
324 }
325 }
326
327 @info_filenames = @ARGV;
328
329 # Check for help option
330 if ($help)
331 {
332 print_usage(*STDOUT);
333 exit(0);
334 }
335
336 # Check for version option
337 if ($version)
338 {
339 print("$tool_name: $lcov_version\n");
340 exit(0);
341 }
342
343 # Check for info filename
344 if (!@info_filenames)
345 {
346 die("No filename specified\n".
347 "Use $tool_name --help to get usage information\n");
348 }
349
350 # Generate a title if none is specified
351 if (!$test_title)
352 {
353 if (scalar(@info_filenames) == 1)
354 {
355 # Only one filename specified, use it as title
356 $test_title = basename($info_filenames[0]);
357 }
358 else
359 {
360 # More than one filename specified, used default title
361 $test_title = "unnamed";
362 }
363 }
364
365 # Make sure css_filename is an absolute path (in case we're changing
366 # directories)
367 if ($css_filename)
368 {
369 if (!($css_filename =~ /^\/(.*)$/))
370 {
371 $css_filename = $cwd."/".$css_filename;
372 }
373 }
374
375 # Make sure tab_size is within valid range
376 if ($tab_size < 1)
377 {
378 print(STDERR "ERROR: invalid number of spaces specified: ".
379 "$tab_size!\n");
380 exit(1);
381 }
382
383 # Get HTML prolog and epilog
384 $html_prolog = get_html_prolog($html_prolog_file);
385 $html_epilog = get_html_epilog($html_epilog_file);
386
387 # Issue a warning if --no-sourceview is enabled together with --frames
388 if ($no_sourceview && defined($frames))
389 {
390 warn("WARNING: option --frames disabled because --no-sourceview ".
391 "was specified!\n");
392 $frames = undef;
393 }
394
395 # Issue a warning if --no-prefix is enabled together with --prefix
396 if ($no_prefix && defined($dir_prefix))
397 {
398 warn("WARNING: option --prefix disabled because --no-prefix was ".
399 "specified!\n");
400 $dir_prefix = undef;
401 }
402
403 if ($sort) {
404 @funcview_sortlist = (0, 1);
405 if ($func_coverage) {
406 @fileview_sortlist = (0, 1, 2);
407 } else {
408 @fileview_sortlist = (0, 1);
409 }
410 } else {
411 @fileview_sortlist = (0);
412 @funcview_sortlist = (0);
413 }
414
415 if ($frames)
416 {
417 # Include genpng code needed for overview image generation
418 do("$tool_dir/genpng");
419 }
420
421 # Make sure output_directory exists, create it if necessary
422 if ($output_directory)
423 {
424 stat($output_directory);
425
426 if (! -e _)
427 {
428 system("mkdir", "-p", $output_directory)
429 and die("ERROR: cannot create directory $_!\n");
430 }
431 }
432
433 # Do something
434 gen_html();
435
436 exit(0);
437
438
439
440 #
441 # print_usage(handle)
442 #
443 # Print usage information.
444 #
445
446 sub print_usage(*)
447 {
448 local *HANDLE = $_[0];
449
450 print(HANDLE <<END_OF_USAGE);
451 Usage: $tool_name [OPTIONS] INFOFILE(S)
452
453 Create HTML output for coverage data found in INFOFILE. Note that INFOFILE
454 may also be a list of filenames.
455
456 Misc:
457 -h, --help Print this help, then exit
458 -v, --version Print version number, then exit
459 -q, --quiet Do not print progress messages
460
461 Operation:
462 -o, --output-directory OUTDIR Write HTML output to OUTDIR
463 -s, --show-details Generate detailed directory view
464 -d, --description-file DESCFILE Read test case descriptions from DESCFILE
465 -k, --keep-descriptions Do not remove unused test descriptions
466 -b, --baseline-file BASEFILE Use BASEFILE as baseline file
467 -p, --prefix PREFIX Remove PREFIX from all directory names
468 --no-prefix Do not remove prefix from directory names
469 --(no-)function-coverage Enable (disable) function coverage display
470
471 HTML output:
472 -f, --frames Use HTML frames for source code view
473 -t, --title TITLE Display TITLE in header of all pages
474 -c, --css-file CSSFILE Use external style sheet file CSSFILE
475 --no-source Do not create source code view
476 --num-spaces NUM Replace tabs with NUM spaces in source view
477 --highlight Highlight lines with converted-only data
478 --legend Include color legend in HTML output
479 --html-prolog FILE Use FILE as HTML prolog for generated pages
480 --html-epilog FILE Use FILE as HTML epilog for generated pages
481 --html-extension EXT Use EXT as filename extension for pages
482 --html-gzip Use gzip to compress HTML
483 --(no-)sort Enable (disable) sorted coverage views
484
485 For more information see: $lcov_url
486 END_OF_USAGE
487 ;
488 }
489
490
491 #
492 # get_rate(found, hit)
493 #
494 # Return a relative value for the specified found&hit values
495 # which is used for sorting the corresponding entries in a
496 # file list.
497 #
498
499 sub get_rate($$)
500 {
501 my ($found, $hit) = @_;
502
503 if ($found == 0) {
504 return 10000;
505 }
506 return int($hit * 1000 / $found) * 10 + 2 - (1 / $found);
507 }
508
509
510 #
511 # gen_html()
512 #
513 # Generate a set of HTML pages from contents of .info file INFO_FILENAME.
514 # Files will be written to the current directory. If provided, test case
515 # descriptions will be read from .tests file TEST_FILENAME and included
516 # in ouput.
517 #
518 # Die on error.
519 #
520
521 sub gen_html()
522 {
523 local *HTML_HANDLE;
524 my %overview;
525 my %base_data;
526 my $lines_found;
527 my $lines_hit;
528 my $fn_found;
529 my $fn_hit;
530 my $overall_found = 0;
531 my $overall_hit = 0;
532 my $total_fn_found = 0;
533 my $total_fn_hit = 0;
534 my $dir_name;
535 my $link_name;
536 my @dir_list;
537 my %new_info;
538
539 # Read in all specified .info files
540 foreach (@info_filenames)
541 {
542 %new_info = %{read_info_file($_)};
543
544 # Combine %new_info with %info_data
545 %info_data = %{combine_info_files(\%info_data, \%new_info)};
546 }
547
548 info("Found %d entries.\n", scalar(keys(%info_data)));
549
550 # Read and apply baseline data if specified
551 if ($base_filename)
552 {
553 # Read baseline file
554 info("Reading baseline file $base_filename\n");
555 %base_data = %{read_info_file($base_filename)};
556 info("Found %d entries.\n", scalar(keys(%base_data)));
557
558 # Apply baseline
559 info("Subtracting baseline data.\n");
560 %info_data = %{apply_baseline(\%info_data, \%base_data)};
561 }
562
563 @dir_list = get_dir_list(keys(%info_data));
564
565 if ($no_prefix)
566 {
567 # User requested that we leave filenames alone
568 info("User asked not to remove filename prefix\n");
569 }
570 elsif (!defined($dir_prefix))
571 {
572 # Get prefix common to most directories in list
573 $dir_prefix = get_prefix(@dir_list);
574
575 if ($dir_prefix)
576 {
577 info("Found common filename prefix \"$dir_prefix\"\n");
578 }
579 else
580 {
581 info("No common filename prefix found!\n");
582 $no_prefix=1;
583 }
584 }
585 else
586 {
587 info("Using user-specified filename prefix \"".
588 "$dir_prefix\"\n");
589 }
590
591 # Read in test description file if specified
592 if ($desc_filename)
593 {
594 info("Reading test description file $desc_filename\n");
595 %test_description = %{read_testfile($desc_filename)};
596
597 # Remove test descriptions which are not referenced
598 # from %info_data if user didn't tell us otherwise
599 if (!$keep_descriptions)
600 {
601 remove_unused_descriptions();
602 }
603 }
604
605 # Change to output directory if specified
606 if ($output_directory)
607 {
608 chdir($output_directory)
609 or die("ERROR: cannot change to directory ".
610 "$output_directory!\n");
611 }
612
613 info("Writing .css and .png files.\n");
614 write_css_file();
615 write_png_files();
616
617 if ($html_gzip)
618 {
619 info("Writing .htaccess file.\n");
620 write_htaccess_file();
621 }
622
623 info("Generating output.\n");
624
625 # Process each subdirectory and collect overview information
626 foreach $dir_name (@dir_list)
627 {
628 ($lines_found, $lines_hit, $fn_found, $fn_hit)
629 = process_dir($dir_name);
630
631 # Remove prefix if applicable
632 if (!$no_prefix && $dir_prefix)
633 {
634 # Match directory names beginning with $dir_prefix
635 $dir_name = apply_prefix($dir_name, $dir_prefix);
636 }
637
638 # Generate name for directory overview HTML page
639 if ($dir_name =~ /^\/(.*)$/)
640 {
641 $link_name = substr($dir_name, 1)."/index.$html_ext";
642 }
643 else
644 {
645 $link_name = $dir_name."/index.$html_ext";
646 }
647
648 $overview{$dir_name} = [$lines_found, $lines_hit, $fn_found,
649 $fn_hit, $link_name,
650 get_rate($lines_found, $lines_hit),
651 get_rate($fn_found, $fn_hit)];
652 $overall_found += $lines_found;
653 $overall_hit += $lines_hit;
654 $total_fn_found += $fn_found;
655 $total_fn_hit += $fn_hit;
656 }
657
658 # Generate overview page
659 info("Writing directory view page.\n");
660
661 # Create sorted pages
662 foreach (@fileview_sortlist) {
663 write_dir_page($fileview_sortname[$_], ".", "", $test_title,
664 undef, $overall_found, $overall_hit,
665 $total_fn_found, $total_fn_hit, \%overview,
666 {}, {}, 0, $_);
667 }
668
669 # Check if there are any test case descriptions to write out
670 if (%test_description)
671 {
672 info("Writing test case description file.\n");
673 write_description_file( \%test_description,
674 $overall_found, $overall_hit,
675 $total_fn_found, $total_fn_hit);
676 }
677
678 chdir($cwd);
679
680 info("Overall coverage rate:\n");
681
682 if ($overall_found == 0)
683 {
684 info(" lines......: no data found\n");
685 return;
686 }
687 info(" lines......: %.1f%% (%d of %d lines)\n",
688 $overall_hit * 100 / $overall_found, $overall_hit,
689 $overall_found,);
690
691 if ($func_coverage)
692 {
693 if ($total_fn_found == 0)
694 {
695 info(" functions..: no data found\n");
696 }
697 else
698 {
699 info(" functions..: %.1f%% (%d of %d functions)\n",
700 $total_fn_hit * 100 / $total_fn_found,
701 $total_fn_hit, $total_fn_found);
702
703 }
704 }
705
706 }
707
708 #
709 # html_create(handle, filename)
710 #
711
712 sub html_create($$)
713 {
714 my $handle = $_[0];
715 my $filename = $_[1];
716
717 if ($html_gzip)
718 {
719 open($handle, "|gzip -c >$filename")
720 or die("ERROR: cannot open $filename for writing ".
721 "(gzip)!\n");
722 }
723 else
724 {
725 open($handle, ">$filename")
726 or die("ERROR: cannot open $filename for writing!\n");
727 }
728 }
729
730 sub write_dir_page($$$$$$$$$$$$$$)
731 {
732 my ($name, $rel_dir, $base_dir, $title, $trunc_dir, $overall_found,
733 $overall_hit, $total_fn_found, $total_fn_hit, $overview,
734 $testhash, $testfnchash, $view_type, $sort_type) = @_;
735
736 # Generate directory overview page including details
737 html_create(*HTML_HANDLE, "$rel_dir/index$name.$html_ext");
738 if (!defined($trunc_dir)) {
739 $trunc_dir = "";
740 }
741 write_html_prolog(*HTML_HANDLE, $base_dir, "LCOV - $title$trunc_dir");
742 write_header(*HTML_HANDLE, $view_type, $trunc_dir, $rel_dir,
743 $overall_found, $overall_hit, $total_fn_found,
744 $total_fn_hit, $sort_type);
745 write_file_table(*HTML_HANDLE, $base_dir, $overview, $testhash,
746 $testfnchash, $view_type, $sort_type);
747 write_html_epilog(*HTML_HANDLE, $base_dir);
748 close(*HTML_HANDLE);
749 }
750
751
752 #
753 # process_dir(dir_name)
754 #
755
756 sub process_dir($)
757 {
758 my $abs_dir = $_[0];
759 my $trunc_dir;
760 my $rel_dir = $abs_dir;
761 my $base_dir;
762 my $filename;
763 my %overview;
764 my $lines_found;
765 my $lines_hit;
766 my $fn_found;
767 my $fn_hit;
768 my $overall_found=0;
769 my $overall_hit=0;
770 my $total_fn_found=0;
771 my $total_fn_hit=0;
772 my $base_name;
773 my $extension;
774 my $testdata;
775 my %testhash;
776 my $testfncdata;
777 my %testfnchash;
778 my @sort_list;
779 local *HTML_HANDLE;
780
781 # Remove prefix if applicable
782 if (!$no_prefix)
783 {
784 # Match directory name beginning with $dir_prefix
785 $rel_dir = apply_prefix($rel_dir, $dir_prefix);
786 }
787
788 $trunc_dir = $rel_dir;
789
790 # Remove leading /
791 if ($rel_dir =~ /^\/(.*)$/)
792 {
793 $rel_dir = substr($rel_dir, 1);
794 }
795
796 $base_dir = get_relative_base_path($rel_dir);
797
798 create_sub_dir($rel_dir);
799
800 # Match filenames which specify files in this directory, not including
801 # sub-directories
802 foreach $filename (grep(/^\Q$abs_dir\E\/[^\/]*$/,keys(%info_data)))
803 {
804 my $page_link;
805 my $func_link;
806
807 ($lines_found, $lines_hit, $fn_found, $fn_hit, $testdata,
808 $testfncdata) = process_file($trunc_dir, $rel_dir, $filename);
809
810 $base_name = basename($filename);
811
812 if ($no_sourceview) {
813 $page_link = "";
814 } elsif ($frames) {
815 # Link to frameset page
816 $page_link = "$base_name.gcov.frameset.$html_ext";
817 } else {
818 # Link directory to source code view page
819 $page_link = "$base_name.gcov.$html_ext";
820 }
821 $overview{$base_name} = [$lines_found, $lines_hit, $fn_found,
822 $fn_hit, $page_link,
823 get_rate($lines_found, $lines_hit),
824 get_rate($fn_found, $fn_hit)];
825
826 $testhash{$base_name} = $testdata;
827 $testfnchash{$base_name} = $testfncdata;
828
829 $overall_found += $lines_found;
830 $overall_hit += $lines_hit;
831
832 $total_fn_found += $fn_found;
833 $total_fn_hit += $fn_hit;
834 }
835
836 # Create sorted pages
837 foreach (@fileview_sortlist) {
838 # Generate directory overview page (without details)
839 write_dir_page($fileview_sortname[$_], $rel_dir, $base_dir,
840 $test_title, $trunc_dir, $overall_found,
841 $overall_hit, $total_fn_found, $total_fn_hit,
842 \%overview, {}, {}, 1, $_);
843 if (!$show_details) {
844 next;
845 }
846 # Generate directory overview page including details
847 write_dir_page("-detail".$fileview_sortname[$_], $rel_dir,
848 $base_dir, $test_title, $trunc_dir,
849 $overall_found, $overall_hit, $total_fn_found,
850 $total_fn_hit, \%overview, \%testhash,
851 \%testfnchash, 1, $_);
852 }
853
854 # Calculate resulting line counts
855 return ($overall_found, $overall_hit, $total_fn_found, $total_fn_hit);
856 }
857
858
859 #
860 # get_converted_lines(testdata)
861 #
862 # Return hash of line numbers of those lines which were only covered in
863 # converted data sets.
864 #
865
866 sub get_converted_lines($)
867 {
868 my $testdata = $_[0];
869 my $testcount;
870 my %converted;
871 my %nonconverted;
872 my $hash;
873 my $testcase;
874 my $line;
875 my %result;
876
877
878 # Get a hash containing line numbers with positive counts both for
879 # converted and original data sets
880 foreach $testcase (keys(%{$testdata}))
881 {
882 # Check to see if this is a converted data set
883 if ($testcase =~ /,diff$/)
884 {
885 $hash = \%converted;
886 }
887 else
888 {
889 $hash = \%nonconverted;
890 }
891
892 $testcount = $testdata->{$testcase};
893 # Add lines with a positive count to hash
894 foreach $line (keys%{$testcount})
895 {
896 if ($testcount->{$line} > 0)
897 {
898 $hash->{$line} = 1;
899 }
900 }
901 }
902
903 # Combine both hashes to resulting list
904 foreach $line (keys(%converted))
905 {
906 if (!defined($nonconverted{$line}))
907 {
908 $result{$line} = 1;
909 }
910 }
911
912 return \%result;
913 }
914
915
916 sub write_function_page($$$$$$$$$$$$$$)
917 {
918 my ($base_dir, $rel_dir, $trunc_dir, $base_name, $title,
919 $lines_found, $lines_hit, $fn_found, $fn_hit,
920 $sumcount, $funcdata, $sumfnccount, $testfncdata, $sort_type) = @_;
921 my $pagetitle;
922 my $filename;
923
924 # Generate function table for this file
925 if ($sort_type == 0) {
926 $filename = "$rel_dir/$base_name.func.$html_ext";
927 } else {
928 $filename = "$rel_dir/$base_name.func-sort-c.$html_ext";
929 }
930 html_create(*HTML_HANDLE, $filename);
931 $pagetitle = "LCOV - $title - $trunc_dir/$base_name - functions";
932 write_html_prolog(*HTML_HANDLE, $base_dir, $pagetitle);
933 write_header(*HTML_HANDLE, 4, "$trunc_dir/$base_name",
934 "$rel_dir/$base_name", $lines_found, $lines_hit,
935 $fn_found, $fn_hit, $sort_type);
936 write_function_table(*HTML_HANDLE, "$base_name.gcov.$html_ext",
937 $sumcount, $funcdata,
938 $sumfnccount, $testfncdata, $base_name,
939 $base_dir, $sort_type);
940 write_html_epilog(*HTML_HANDLE, $base_dir, 1);
941 close(*HTML_HANDLE);
942 }
943
944
945 #
946 # process_file(trunc_dir, rel_dir, filename)
947 #
948
949 sub process_file($$$)
950 {
951 info("Processing file ".apply_prefix($_[2], $dir_prefix)."\n");
952
953 my $trunc_dir = $_[0];
954 my $rel_dir = $_[1];
955 my $filename = $_[2];
956 my $base_name = basename($filename);
957 my $base_dir = get_relative_base_path($rel_dir);
958 my $testdata;
959 my $testcount;
960 my $sumcount;
961 my $funcdata;
962 my $checkdata;
963 my $testfncdata;
964 my $sumfnccount;
965 my $lines_found;
966 my $lines_hit;
967 my $fn_found;
968 my $fn_hit;
969 my $converted;
970 my @source;
971 my $pagetitle;
972 local *HTML_HANDLE;
973
974 ($testdata, $sumcount, $funcdata, $checkdata, $testfncdata,
975 $sumfnccount, $lines_found, $lines_hit, $fn_found, $fn_hit)
976 = get_info_entry($info_data{$filename});
977
978 # Return after this point in case user asked us not to generate
979 # source code view
980 if ($no_sourceview)
981 {
982 return ($lines_found, $lines_hit,
983 $fn_found, $fn_hit, $testdata);
984 }
985
986 $converted = get_converted_lines($testdata);
987 # Generate source code view for this file
988 html_create(*HTML_HANDLE, "$rel_dir/$base_name.gcov.$html_ext");
989 $pagetitle = "LCOV - $test_title - $trunc_dir/$base_name";
990 write_html_prolog(*HTML_HANDLE, $base_dir, $pagetitle);
991 write_header(*HTML_HANDLE, 2, "$trunc_dir/$base_name",
992 "$rel_dir/$base_name", $lines_found, $lines_hit,
993 $fn_found, $fn_hit, 0);
994 @source = write_source(*HTML_HANDLE, $filename, $sumcount, $checkdata,
995 $converted, $funcdata);
996
997 write_html_epilog(*HTML_HANDLE, $base_dir, 1);
998 close(*HTML_HANDLE);
999
1000 if ($func_coverage) {
1001 # Create function tables
1002 foreach (@funcview_sortlist) {
1003 write_function_page($base_dir, $rel_dir, $trunc_dir,
1004 $base_name, $test_title,
1005 $lines_found, $lines_hit,
1006 $fn_found, $fn_hit, $sumcount,
1007 $funcdata, $sumfnccount,
1008 $testfncdata, $_);
1009 }
1010 }
1011
1012 # Additional files are needed in case of frame output
1013 if (!$frames)
1014 {
1015 return ($lines_found, $lines_hit,
1016 $fn_found, $fn_hit, $testdata);
1017 }
1018
1019 # Create overview png file
1020 gen_png("$rel_dir/$base_name.gcov.png", $overview_width, $tab_size,
1021 @source);
1022
1023 # Create frameset page
1024 html_create(*HTML_HANDLE,
1025 "$rel_dir/$base_name.gcov.frameset.$html_ext");
1026 write_frameset(*HTML_HANDLE, $base_dir, $base_name, $pagetitle);
1027 close(*HTML_HANDLE);
1028
1029 # Write overview frame
1030 html_create(*HTML_HANDLE,
1031 "$rel_dir/$base_name.gcov.overview.$html_ext");
1032 write_overview(*HTML_HANDLE, $base_dir, $base_name, $pagetitle,
1033 scalar(@source));
1034 close(*HTML_HANDLE);
1035
1036 return ($lines_found, $lines_hit, $fn_found, $fn_hit, $testdata,
1037 $testfncdata);
1038 }
1039
1040
1041 #
1042 # read_info_file(info_filename)
1043 #
1044 # Read in the contents of the .info file specified by INFO_FILENAME. Data will
1045 # be returned as a reference to a hash containing the following mappings:
1046 #
1047 # %result: for each filename found in file -> \%data
1048 #
1049 # %data: "test" -> \%testdata
1050 # "sum" -> \%sumcount
1051 # "func" -> \%funcdata
1052 # "found" -> $lines_found (number of instrumented lines found in file)
1053 # "hit" -> $lines_hit (number of executed lines in file)
1054 # "check" -> \%checkdata
1055 # "testfnc" -> \%testfncdata
1056 # "sumfnc" -> \%sumfnccount
1057 #
1058 # %testdata : name of test affecting this file -> \%testcount
1059 # %testfncdata: name of test affecting this file -> \%testfnccount
1060 #
1061 # %testcount : line number -> execution count for a single test
1062 # %testfnccount: function name -> execution count for a single test
1063 # %sumcount : line number -> execution count for all tests
1064 # %sumfnccount : function name -> execution count for all tests
1065 # %funcdata : function name -> line number
1066 # %checkdata : line number -> checksum of source code line
1067 #
1068 # Note that .info file sections referring to the same file and test name
1069 # will automatically be combined by adding all execution counts.
1070 #
1071 # Note that if INFO_FILENAME ends with ".gz", it is assumed that the file
1072 # is compressed using GZIP. If available, GUNZIP will be used to decompress
1073 # this file.
1074 #
1075 # Die on error.
1076 #
1077
1078 sub read_info_file($)
1079 {
1080 my $tracefile = $_[0]; # Name of tracefile
1081 my %result; # Resulting hash: file -> data
1082 my $data; # Data handle for current entry
1083 my $testdata; # " "
1084 my $testcount; # " "
1085 my $sumcount; # " "
1086 my $funcdata; # " "
1087 my $checkdata; # " "
1088 my $testfncdata;
1089 my $testfnccount;
1090 my $sumfnccount;
1091 my $line; # Current line read from .info file
1092 my $testname; # Current test name
1093 my $filename; # Current filename
1094 my $hitcount; # Count for lines hit
1095 my $count; # Execution count of current line
1096 my $negative; # If set, warn about negative counts
1097 my $changed_testname; # If set, warn about changed testname
1098 my $line_checksum; # Checksum of current line
1099 local *INFO_HANDLE; # Filehandle for .info file
1100
1101 info("Reading data file $tracefile\n");
1102
1103 # Check if file exists and is readable
1104 stat($_[0]);
1105 if (!(-r _))
1106 {
1107 die("ERROR: cannot read file $_[0]!\n");
1108 }
1109
1110 # Check if this is really a plain file
1111 if (!(-f _))
1112 {
1113 die("ERROR: not a plain file: $_[0]!\n");
1114 }
1115
1116 # Check for .gz extension
1117 if ($_[0] =~ /\.gz$/)
1118 {
1119 # Check for availability of GZIP tool
1120 system_no_output(1, "gunzip" ,"-h")
1121 and die("ERROR: gunzip command not available!\n");
1122
1123 # Check integrity of compressed file
1124 system_no_output(1, "gunzip", "-t", $_[0])
1125 and die("ERROR: integrity check failed for ".
1126 "compressed file $_[0]!\n");
1127
1128 # Open compressed file
1129 open(INFO_HANDLE, "gunzip -c $_[0]|")
1130 or die("ERROR: cannot start gunzip to decompress ".
1131 "file $_[0]!\n");
1132 }
1133 else
1134 {
1135 # Open decompressed file
1136 open(INFO_HANDLE, $_[0])
1137 or die("ERROR: cannot read file $_[0]!\n");
1138 }
1139
1140 $testname = "";
1141 while (<INFO_HANDLE>)
1142 {
1143 chomp($_);
1144 $line = $_;
1145
1146 # Switch statement
1147 foreach ($line)
1148 {
1149 /^TN:([^,]*)/ && do
1150 {
1151 # Test name information found
1152 $testname = defined($1) ? $1 : "";
1153 if ($testname =~ s/\W/_/g)
1154 {
1155 $changed_testname = 1;
1156 }
1157 last;
1158 };
1159
1160 /^[SK]F:(.*)/ && do
1161 {
1162 # Filename information found
1163 # Retrieve data for new entry
1164 $filename = $1;
1165
1166 $data = $result{$filename};
1167 ($testdata, $sumcount, $funcdata, $checkdata,
1168 $testfncdata, $sumfnccount) =
1169 get_info_entry($data);
1170
1171 if (defined($testname))
1172 {
1173 $testcount = $testdata->{$testname};
1174 $testfnccount = $testfncdata->{$testname };
1175 }
1176 else
1177 {
1178 $testcount = {};
1179 $testfnccount = {};
1180 }
1181 last;
1182 };
1183
1184 /^DA:(\d+),(-?\d+)(,[^,\s]+)?/ && do
1185 {
1186 # Fix negative counts
1187 $count = $2 < 0 ? 0 : $2;
1188 if ($2 < 0)
1189 {
1190 $negative = 1;
1191 }
1192 # Execution count found, add to structure
1193 # Add summary counts
1194 $sumcount->{$1} += $count;
1195
1196 # Add test-specific counts
1197 if (defined($testname))
1198 {
1199 $testcount->{$1} += $count;
1200 }
1201
1202 # Store line checksum if available
1203 if (defined($3))
1204 {
1205 $line_checksum = substr($3, 1);
1206
1207 # Does it match a previous definition
1208 if (defined($checkdata->{$1}) &&
1209 ($checkdata->{$1} ne
1210 $line_checksum))
1211 {
1212 die("ERROR: checksum mismatch ".
1213 "at $filename:$1\n");
1214 }
1215
1216 $checkdata->{$1} = $line_checksum;
1217 }
1218 last;
1219 };
1220
1221 /^FN:(\d+),([^,]+)/ && do
1222 {
1223 # Function data found, add to structure
1224 $funcdata->{$2} = $1;
1225
1226 # Also initialize function call data
1227 if (!defined($sumfnccount->{$2})) {
1228 $sumfnccount->{$2} = 0;
1229 }
1230 if (defined($testname))
1231 {
1232 if (!defined($testfnccount->{$2})) {
1233 $testfnccount->{$2} = 0;
1234 }
1235 }
1236 last;
1237 };
1238
1239 /^FNDA:(\d+),([^,]+)/ && do
1240 {
1241 # Function call count found, add to structure
1242 # Add summary counts
1243 $sumfnccount->{$2} += $1;
1244
1245 # Add test-specific counts
1246 if (defined($testname))
1247 {
1248 $testfnccount->{$2} += $1;
1249 }
1250 last;
1251 };
1252 /^end_of_record/ && do
1253 {
1254 # Found end of section marker
1255 if ($filename)
1256 {
1257 # Store current section data
1258 if (defined($testname))
1259 {
1260 $testdata->{$testname} =
1261 $testcount;
1262 $testfncdata->{$testname} =
1263 $testfnccount;
1264 }
1265
1266 set_info_entry($data, $testdata,
1267 $sumcount, $funcdata,
1268 $checkdata, $testfncdata,
1269 $sumfnccount);
1270 $result{$filename} = $data;
1271 last;
1272 }
1273 };
1274
1275 # default
1276 last;
1277 }
1278 }
1279 close(INFO_HANDLE);
1280
1281 # Calculate lines_found and lines_hit for each file
1282 foreach $filename (keys(%result))
1283 {
1284 $data = $result{$filename};
1285
1286 ($testdata, $sumcount, undef, undef, $testfncdata,
1287 $sumfnccount) = get_info_entry($data);
1288
1289 # Filter out empty files
1290 if (scalar(keys(%{$sumcount})) == 0)
1291 {
1292 delete($result{$filename});
1293 next;
1294 }
1295 # Filter out empty test cases
1296 foreach $testname (keys(%{$testdata}))
1297 {
1298 if (!defined($testdata->{$testname}) ||
1299 scalar(keys(%{$testdata->{$testname}})) == 0)
1300 {
1301 delete($testdata->{$testname});
1302 delete($testfncdata->{$testname});
1303 }
1304 }
1305
1306 $data->{"found"} = scalar(keys(%{$sumcount}));
1307 $hitcount = 0;
1308
1309 foreach (keys(%{$sumcount}))
1310 {
1311 if ($sumcount->{$_} > 0) { $hitcount++; }
1312 }
1313
1314 $data->{"hit"} = $hitcount;
1315
1316 # Get found/hit values for function call data
1317 $data->{"f_found"} = scalar(keys(%{$sumfnccount}));
1318 $hitcount = 0;
1319
1320 foreach (keys(%{$sumfnccount})) {
1321 if ($sumfnccount->{$_} > 0) {
1322 $hitcount++;
1323 }
1324 }
1325 $data->{"f_hit"} = $hitcount;
1326 }
1327
1328 if (scalar(keys(%result)) == 0)
1329 {
1330 die("ERROR: no valid records found in tracefile $tracefile\n");
1331 }
1332 if ($negative)
1333 {
1334 warn("WARNING: negative counts found in tracefile ".
1335 "$tracefile\n");
1336 }
1337 if ($changed_testname)
1338 {
1339 warn("WARNING: invalid characters removed from testname in ".
1340 "tracefile $tracefile\n");
1341 }
1342
1343 return(\%result);
1344 }
1345
1346
1347 #
1348 # get_info_entry(hash_ref)
1349 #
1350 # Retrieve data from an entry of the structure generated by read_info_file().
1351 # Return a list of references to hashes:
1352 # (test data hash ref, sum count hash ref, funcdata hash ref, checkdata hash
1353 # ref, testfncdata hash ref, sumfnccount hash ref, lines found, lines hit,
1354 # functions found, functions hit)
1355 #
1356
1357 sub get_info_entry($)
1358 {
1359 my $testdata_ref = $_[0]->{"test"};
1360 my $sumcount_ref = $_[0]->{"sum"};
1361 my $funcdata_ref = $_[0]->{"func"};
1362 my $checkdata_ref = $_[0]->{"check"};
1363 my $testfncdata = $_[0]->{"testfnc"};
1364 my $sumfnccount = $_[0]->{"sumfnc"};
1365 my $lines_found = $_[0]->{"found"};
1366 my $lines_hit = $_[0]->{"hit"};
1367 my $fn_found = $_[0]->{"f_found"};
1368 my $fn_hit = $_[0]->{"f_hit"};
1369
1370 return ($testdata_ref, $sumcount_ref, $funcdata_ref, $checkdata_ref,
1371 $testfncdata, $sumfnccount, $lines_found, $lines_hit,
1372 $fn_found, $fn_hit);
1373 }
1374
1375
1376 #
1377 # set_info_entry(hash_ref, testdata_ref, sumcount_ref, funcdata_ref,
1378 # checkdata_ref, testfncdata_ref, sumfcncount_ref[,lines_found,
1379 # lines_hit, f_found, f_hit])
1380 #
1381 # Update the hash referenced by HASH_REF with the provided data references.
1382 #
1383
1384 sub set_info_entry($$$$$$$;$$$$)
1385 {
1386 my $data_ref = $_[0];
1387
1388 $data_ref->{"test"} = $_[1];
1389 $data_ref->{"sum"} = $_[2];
1390 $data_ref->{"func"} = $_[3];
1391 $data_ref->{"check"} = $_[4];
1392 $data_ref->{"testfnc"} = $_[5];
1393 $data_ref->{"sumfnc"} = $_[6];
1394
1395 if (defined($_[7])) { $data_ref->{"found"} = $_[7]; }
1396 if (defined($_[8])) { $data_ref->{"hit"} = $_[8]; }
1397 if (defined($_[9])) { $data_ref->{"f_found"} = $_[9]; }
1398 if (defined($_[10])) { $data_ref->{"f_hit"} = $_[10]; }
1399 }
1400
1401
1402 #
1403 # add_counts(data1_ref, data2_ref)
1404 #
1405 # DATA1_REF and DATA2_REF are references to hashes containing a mapping
1406 #
1407 # line number -> execution count
1408 #
1409 # Return a list (RESULT_REF, LINES_FOUND, LINES_HIT) where RESULT_REF
1410 # is a reference to a hash containing the combined mapping in which
1411 # execution counts are added.
1412 #
1413
1414 sub add_counts($$)
1415 {
1416 my %data1 = %{$_[0]}; # Hash 1
1417 my %data2 = %{$_[1]}; # Hash 2
1418 my %result; # Resulting hash
1419 my $line; # Current line iteration scalar
1420 my $data1_count; # Count of line in hash1
1421 my $data2_count; # Count of line in hash2
1422 my $found = 0; # Total number of lines found
1423 my $hit = 0; # Number of lines with a count > 0
1424
1425 foreach $line (keys(%data1))
1426 {
1427 $data1_count = $data1{$line};
1428 $data2_count = $data2{$line};
1429
1430 # Add counts if present in both hashes
1431 if (defined($data2_count)) { $data1_count += $data2_count; }
1432
1433 # Store sum in %result
1434 $result{$line} = $data1_count;
1435
1436 $found++;
1437 if ($data1_count > 0) { $hit++; }
1438 }
1439
1440 # Add lines unique to data2
1441 foreach $line (keys(%data2))
1442 {
1443 # Skip lines already in data1
1444 if (defined($data1{$line})) { next; }
1445
1446 # Copy count from data2
1447 $result{$line} = $data2{$line};
1448
1449 $found++;
1450 if ($result{$line} > 0) { $hit++; }
1451 }
1452
1453 return (\%result, $found, $hit);
1454 }
1455
1456
1457 #
1458 # merge_checksums(ref1, ref2, filename)
1459 #
1460 # REF1 and REF2 are references to hashes containing a mapping
1461 #
1462 # line number -> checksum
1463 #
1464 # Merge checksum lists defined in REF1 and REF2 and return reference to
1465 # resulting hash. Die if a checksum for a line is defined in both hashes
1466 # but does not match.
1467 #
1468
1469 sub merge_checksums($$$)
1470 {
1471 my $ref1 = $_[0];
1472 my $ref2 = $_[1];
1473 my $filename = $_[2];
1474 my %result;
1475 my $line;
1476
1477 foreach $line (keys(%{$ref1}))
1478 {
1479 if (defined($ref2->{$line}) &&
1480 ($ref1->{$line} ne $ref2->{$line}))
1481 {
1482 die("ERROR: checksum mismatch at $filename:$line\n");
1483 }
1484 $result{$line} = $ref1->{$line};
1485 }
1486
1487 foreach $line (keys(%{$ref2}))
1488 {
1489 $result{$line} = $ref2->{$line};
1490 }
1491
1492 return \%result;
1493 }
1494
1495
1496 #
1497 # merge_func_data(funcdata1, funcdata2, filename)
1498 #
1499
1500 sub merge_func_data($$$)
1501 {
1502 my ($funcdata1, $funcdata2, $filename) = @_;
1503 my %result;
1504 my $func;
1505
1506 %result = %{$funcdata1};
1507
1508 foreach $func (keys(%{$funcdata2})) {
1509 my $line1 = $result{$func};
1510 my $line2 = $funcdata2->{$func};
1511
1512 if (defined($line1) && ($line1 != $line2)) {
1513 warn("WARNING: function data mismatch at ".
1514 "$filename:$line2\n");
1515 next;
1516 }
1517 $result{$func} = $line2;
1518 }
1519
1520 return \%result;
1521 }
1522
1523
1524 #
1525 # add_fnccount(fnccount1, fnccount2)
1526 #
1527 # Add function call count data. Return list (fnccount_added, f_found, f_hit)
1528 #
1529
1530 sub add_fnccount($$)
1531 {
1532 my ($fnccount1, $fnccount2) = @_;
1533 my %result;
1534 my $fn_found;
1535 my $fn_hit;
1536 my $function;
1537
1538 %result = %{$fnccount1};
1539 foreach $function (keys(%{$fnccount2})) {
1540 $result{$function} += $fnccount2->{$function};
1541 }
1542 $fn_found = scalar(keys(%result));
1543 $fn_hit = 0;
1544 foreach $function (keys(%result)) {
1545 if ($result{$function} > 0) {
1546 $fn_hit++;
1547 }
1548 }
1549
1550 return (\%result, $fn_found, $fn_hit);
1551 }
1552
1553 #
1554 # add_testfncdata(testfncdata1, testfncdata2)
1555 #
1556 # Add function call count data for several tests. Return reference to
1557 # added_testfncdata.
1558 #
1559
1560 sub add_testfncdata($$)
1561 {
1562 my ($testfncdata1, $testfncdata2) = @_;
1563 my %result;
1564 my $testname;
1565
1566 foreach $testname (keys(%{$testfncdata1})) {
1567 if (defined($testfncdata2->{$testname})) {
1568 my $fnccount;
1569
1570 # Function call count data for this testname exists
1571 # in both data sets: add
1572 ($fnccount) = add_fnccount(
1573 $testfncdata1->{$testname},
1574 $testfncdata2->{$testname});
1575 $result{$testname} = $fnccount;
1576 next;
1577 }
1578 # Function call count data for this testname is unique to
1579 # data set 1: copy
1580 $result{$testname} = $testfncdata1->{$testname};
1581 }
1582
1583 # Add count data for testnames unique to data set 2
1584 foreach $testname (keys(%{$testfncdata2})) {
1585 if (!defined($result{$testname})) {
1586 $result{$testname} = $testfncdata2->{$testname};
1587 }
1588 }
1589 return \%result;
1590 }
1591
1592 #
1593 # combine_info_entries(entry_ref1, entry_ref2, filename)
1594 #
1595 # Combine .info data entry hashes referenced by ENTRY_REF1 and ENTRY_REF2.
1596 # Return reference to resulting hash.
1597 #
1598
1599 sub combine_info_entries($$$)
1600 {
1601 my $entry1 = $_[0]; # Reference to hash containing first entry
1602 my $testdata1;
1603 my $sumcount1;
1604 my $funcdata1;
1605 my $checkdata1;
1606 my $testfncdata1;
1607 my $sumfnccount1;
1608
1609 my $entry2 = $_[1]; # Reference to hash containing second entry
1610 my $testdata2;
1611 my $sumcount2;
1612 my $funcdata2;
1613 my $checkdata2;
1614 my $testfncdata2;
1615 my $sumfnccount2;
1616
1617 my %result; # Hash containing combined entry
1618 my %result_testdata;
1619 my $result_sumcount = {};
1620 my $result_funcdata;
1621 my $result_testfncdata;
1622 my $result_sumfnccount;
1623 my $lines_found;
1624 my $lines_hit;
1625 my $fn_found;
1626 my $fn_hit;
1627
1628 my $testname;
1629 my $filename = $_[2];
1630
1631 # Retrieve data
1632 ($testdata1, $sumcount1, $funcdata1, $checkdata1, $testfncdata1,
1633 $sumfnccount1) = get_info_entry($entry1);
1634 ($testdata2, $sumcount2, $funcdata2, $checkdata2, $testfncdata2,
1635 $sumfnccount2) = get_info_entry($entry2);
1636
1637 # Merge checksums
1638 $checkdata1 = merge_checksums($checkdata1, $checkdata2, $filename);
1639
1640 # Combine funcdata
1641 $result_funcdata = merge_func_data($funcdata1, $funcdata2, $filename);
1642
1643 # Combine function call count data
1644 $result_testfncdata = add_testfncdata($testfncdata1, $testfncdata2);
1645 ($result_sumfnccount, $fn_found, $fn_hit) =
1646 add_fnccount($sumfnccount1, $sumfnccount2);
1647
1648 # Combine testdata
1649 foreach $testname (keys(%{$testdata1}))
1650 {
1651 if (defined($testdata2->{$testname}))
1652 {
1653 # testname is present in both entries, requires
1654 # combination
1655 ($result_testdata{$testname}) =
1656 add_counts($testdata1->{$testname},
1657 $testdata2->{$testname});
1658 }
1659 else
1660 {
1661 # testname only present in entry1, add to result
1662 $result_testdata{$testname} = $testdata1->{$testname};
1663 }
1664
1665 # update sum count hash
1666 ($result_sumcount, $lines_found, $lines_hit) =
1667 add_counts($result_sumcount,
1668 $result_testdata{$testname});
1669 }
1670
1671 foreach $testname (keys(%{$testdata2}))
1672 {
1673 # Skip testnames already covered by previous iteration
1674 if (defined($testdata1->{$testname})) { next; }
1675
1676 # testname only present in entry2, add to result hash
1677 $result_testdata{$testname} = $testdata2->{$testname};
1678
1679 # update sum count hash
1680 ($result_sumcount, $lines_found, $lines_hit) =
1681 add_counts($result_sumcount,
1682 $result_testdata{$testname});
1683 }
1684
1685 # Calculate resulting sumcount
1686
1687 # Store result
1688 set_info_entry(\%result, \%result_testdata, $result_sumcount,
1689 $result_funcdata, $checkdata1, $result_testfncdata,
1690 $result_sumfnccount, $lines_found, $lines_hit,
1691 $fn_found, $fn_hit);
1692
1693 return(\%result);
1694 }
1695
1696
1697 #
1698 # combine_info_files(info_ref1, info_ref2)
1699 #
1700 # Combine .info data in hashes referenced by INFO_REF1 and INFO_REF2. Return
1701 # reference to resulting hash.
1702 #
1703
1704 sub combine_info_files($$)
1705 {
1706 my %hash1 = %{$_[0]};
1707 my %hash2 = %{$_[1]};
1708 my $filename;
1709
1710 foreach $filename (keys(%hash2))
1711 {
1712 if ($hash1{$filename})
1713 {
1714 # Entry already exists in hash1, combine them
1715 $hash1{$filename} =
1716 combine_info_entries($hash1{$filename},
1717 $hash2{$filename},
1718 $filename);
1719 }
1720 else
1721 {
1722 # Entry is unique in both hashes, simply add to
1723 # resulting hash
1724 $hash1{$filename} = $hash2{$filename};
1725 }
1726 }
1727
1728 return(\%hash1);
1729 }
1730
1731
1732 #
1733 # get_prefix(filename_list)
1734 #
1735 # Search FILENAME_LIST for a directory prefix which is common to as many
1736 # list entries as possible, so that removing this prefix will minimize the
1737 # sum of the lengths of all resulting shortened filenames.
1738 #
1739
1740 sub get_prefix(@)
1741 {
1742 my @filename_list = @_; # provided list of filenames
1743 my %prefix; # mapping: prefix -> sum of lengths
1744 my $current; # Temporary iteration variable
1745
1746 # Find list of prefixes
1747 foreach (@filename_list)
1748 {
1749 # Need explicit assignment to get a copy of $_ so that
1750 # shortening the contained prefix does not affect the list
1751 $current = shorten_prefix($_);
1752 while ($current = shorten_prefix($current))
1753 {
1754 # Skip rest if the remaining prefix has already been
1755 # added to hash
1756 if ($prefix{$current}) { last; }
1757
1758 # Initialize with 0
1759 $prefix{$current}="0";
1760 }
1761
1762 }
1763
1764 # Calculate sum of lengths for all prefixes
1765 foreach $current (keys(%prefix))
1766 {
1767 foreach (@filename_list)
1768 {
1769 # Add original length
1770 $prefix{$current} += length($_);
1771
1772 # Check whether prefix matches
1773 if (substr($_, 0, length($current)) eq $current)
1774 {
1775 # Subtract prefix length for this filename
1776 $prefix{$current} -= length($current);
1777 }
1778 }
1779 }
1780
1781 # Find and return prefix with minimal sum
1782 $current = (keys(%prefix))[0];
1783
1784 foreach (keys(%prefix))
1785 {
1786 if ($prefix{$_} < $prefix{$current})
1787 {
1788 $current = $_;
1789 }
1790 }
1791
1792 return($current);
1793 }
1794
1795
1796 #
1797 # shorten_prefix(prefix)
1798 #
1799 # Return PREFIX shortened by last directory component.
1800 #
1801
1802 sub shorten_prefix($)
1803 {
1804 my @list = split("/", $_[0]);
1805
1806 pop(@list);
1807 return join("/", @list);
1808 }
1809
1810
1811
1812 #
1813 # get_dir_list(filename_list)
1814 #
1815 # Return sorted list of directories for each entry in given FILENAME_LIST.
1816 #
1817
1818 sub get_dir_list(@)
1819 {
1820 my %result;
1821
1822 foreach (@_)
1823 {
1824 $result{shorten_prefix($_)} = "";
1825 }
1826
1827 return(sort(keys(%result)));
1828 }
1829
1830
1831 #
1832 # get_relative_base_path(subdirectory)
1833 #
1834 # Return a relative path string which references the base path when applied
1835 # in SUBDIRECTORY.
1836 #
1837 # Example: get_relative_base_path("fs/mm") -> "../../"
1838 #
1839
1840 sub get_relative_base_path($)
1841 {
1842 my $result = "";
1843 my $index;
1844
1845 # Make an empty directory path a special case
1846 if (!$_[0]) { return(""); }
1847
1848 # Count number of /s in path
1849 $index = ($_[0] =~ s/\//\//g);
1850
1851 # Add a ../ to $result for each / in the directory path + 1
1852 for (; $index>=0; $index--)
1853 {
1854 $result .= "../";
1855 }
1856
1857 return $result;
1858 }
1859
1860
1861 #
1862 # read_testfile(test_filename)
1863 #
1864 # Read in file TEST_FILENAME which contains test descriptions in the format:
1865 #
1866 # TN:<whitespace><test name>
1867 # TD:<whitespace><test description>
1868 #
1869 # for each test case. Return a reference to a hash containing a mapping
1870 #
1871 # test name -> test description.
1872 #
1873 # Die on error.
1874 #
1875
1876 sub read_testfile($)
1877 {
1878 my %result;
1879 my $test_name;
1880 my $changed_testname;
1881 local *TEST_HANDLE;
1882
1883 open(TEST_HANDLE, "<".$_[0])
1884 or die("ERROR: cannot open $_[0]!\n");
1885
1886 while (<TEST_HANDLE>)
1887 {
1888 chomp($_);
1889
1890 # Match lines beginning with TN:<whitespace(s)>
1891 if (/^TN:\s+(.*?)\s*$/)
1892 {
1893 # Store name for later use
1894 $test_name = $1;
1895 if ($test_name =~ s/\W/_/g)
1896 {
1897 $changed_testname = 1;
1898 }
1899 }
1900
1901 # Match lines beginning with TD:<whitespace(s)>
1902 if (/^TD:\s+(.*?)\s*$/)
1903 {
1904 # Check for empty line
1905 if ($1)
1906 {
1907 # Add description to hash
1908 $result{$test_name} .= " $1";
1909 }
1910 else
1911 {
1912 # Add empty line
1913 $result{$test_name} .= "\n\n";
1914 }
1915 }
1916 }
1917
1918 close(TEST_HANDLE);
1919
1920 if ($changed_testname)
1921 {
1922 warn("WARNING: invalid characters removed from testname in ".
1923 "descriptions file $_[0]\n");
1924 }
1925
1926 return \%result;
1927 }
1928
1929
1930 #
1931 # escape_html(STRING)
1932 #
1933 # Return a copy of STRING in which all occurrences of HTML special characters
1934 # are escaped.
1935 #
1936
1937 sub escape_html($)
1938 {
1939 my $string = $_[0];
1940
1941 if (!$string) { return ""; }
1942
1943 $string =~ s/&/&amp;/g; # & -> &amp;
1944 $string =~ s/</&lt;/g; # < -> &lt;
1945 $string =~ s/>/&gt;/g; # > -> &gt;
1946 $string =~ s/\"/&quot;/g; # " -> &quot;
1947
1948 while ($string =~ /^([^\t]*)(\t)/)
1949 {
1950 my $replacement = " "x($tab_size - (length($1) % $tab_size));
1951 $string =~ s/^([^\t]*)(\t)/$1$replacement/;
1952 }
1953
1954 $string =~ s/\n/<br>/g; # \n -> <br>
1955
1956 return $string;
1957 }
1958
1959
1960 #
1961 # get_date_string()
1962 #
1963 # Return the current date in the form: yyyy-mm-dd
1964 #
1965
1966 sub get_date_string()
1967 {
1968 my $year;
1969 my $month;
1970 my $day;
1971
1972 ($year, $month, $day) = (localtime())[5, 4, 3];
1973
1974 return sprintf("%d-%02d-%02d", $year+1900, $month+1, $day);
1975 }
1976
1977
1978 #
1979 # create_sub_dir(dir_name)
1980 #
1981 # Create subdirectory DIR_NAME if it does not already exist, including all its
1982 # parent directories.
1983 #
1984 # Die on error.
1985 #
1986
1987 sub create_sub_dir($)
1988 {
1989 system("mkdir", "-p" ,$_[0])
1990 and die("ERROR: cannot create directory $_!\n");
1991 }
1992
1993
1994 #
1995 # write_description_file(descriptions, overall_found, overall_hit,
1996 # total_fn_found, total_fn_hit)
1997 #
1998 # Write HTML file containing all test case descriptions. DESCRIPTIONS is a
1999 # reference to a hash containing a mapping
2000 #
2001 # test case name -> test case description
2002 #
2003 # Die on error.
2004 #
2005
2006 sub write_description_file($$$$$)
2007 {
2008 my %description = %{$_[0]};
2009 my $found = $_[1];
2010 my $hit = $_[2];
2011 my $fn_found = $_[3];
2012 my $fn_hit = $_[4];
2013 my $test_name;
2014 local *HTML_HANDLE;
2015
2016 html_create(*HTML_HANDLE,"descriptions.$html_ext");
2017 write_html_prolog(*HTML_HANDLE, "", "LCOV - test case descriptions");
2018 write_header(*HTML_HANDLE, 3, "", "", $found, $hit, $fn_found,
2019 $fn_hit, 0);
2020
2021 write_test_table_prolog(*HTML_HANDLE,
2022 "Test case descriptions - alphabetical list");
2023
2024 foreach $test_name (sort(keys(%description)))
2025 {
2026 write_test_table_entry(*HTML_HANDLE, $test_name,
2027 escape_html($description{$test_name}));
2028 }
2029
2030 write_test_table_epilog(*HTML_HANDLE);
2031 write_html_epilog(*HTML_HANDLE, "");
2032
2033 close(*HTML_HANDLE);
2034 }
2035
2036
2037
2038 #
2039 # write_png_files()
2040 #
2041 # Create all necessary .png files for the HTML-output in the current
2042 # directory. .png-files are used as bar graphs.
2043 #
2044 # Die on error.
2045 #
2046
2047 sub write_png_files()
2048 {
2049 my %data;
2050 local *PNG_HANDLE;
2051
2052 $data{"ruby.png"} =
2053 [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00,
2054 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01,
2055 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x00, 0x00, 0x00, 0x25,
2056 0xdb, 0x56, 0xca, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d,
2057 0x45, 0x07, 0xd2, 0x07, 0x11, 0x0f, 0x18, 0x10, 0x5d, 0x57,
2058 0x34, 0x6e, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73,
2059 0x00, 0x00, 0x0b, 0x12, 0x00, 0x00, 0x0b, 0x12, 0x01, 0xd2,
2060 0xdd, 0x7e, 0xfc, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d,
2061 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00,
2062 0x00, 0x00, 0x06, 0x50, 0x4c, 0x54, 0x45, 0xff, 0x35, 0x2f,
2063 0x00, 0x00, 0x00, 0xd0, 0x33, 0x9a, 0x9d, 0x00, 0x00, 0x00,
2064 0x0a, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x60, 0x00,
2065 0x00, 0x00, 0x02, 0x00, 0x01, 0xe5, 0x27, 0xde, 0xfc, 0x00,
2066 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60,
2067 0x82];
2068 $data{"amber.png"} =
2069 [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00,
2070 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01,
2071 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x00, 0x00, 0x00, 0x25,
2072 0xdb, 0x56, 0xca, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d,
2073 0x45, 0x07, 0xd2, 0x07, 0x11, 0x0f, 0x28, 0x04, 0x98, 0xcb,
2074 0xd6, 0xe0, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73,
2075 0x00, 0x00, 0x0b, 0x12, 0x00, 0x00, 0x0b, 0x12, 0x01, 0xd2,
2076 0xdd, 0x7e, 0xfc, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d,
2077 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00,
2078 0x00, 0x00, 0x06, 0x50, 0x4c, 0x54, 0x45, 0xff, 0xe0, 0x50,
2079 0x00, 0x00, 0x00, 0xa2, 0x7a, 0xda, 0x7e, 0x00, 0x00, 0x00,
2080 0x0a, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x60, 0x00,
2081 0x00, 0x00, 0x02, 0x00, 0x01, 0xe5, 0x27, 0xde, 0xfc, 0x00,
2082 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60,
2083 0x82];
2084 $data{"emerald.png"} =
2085 [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00,
2086 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01,
2087 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x00, 0x00, 0x00, 0x25,
2088 0xdb, 0x56, 0xca, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d,
2089 0x45, 0x07, 0xd2, 0x07, 0x11, 0x0f, 0x22, 0x2b, 0xc9, 0xf5,
2090 0x03, 0x33, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73,
2091 0x00, 0x00, 0x0b, 0x12, 0x00, 0x00, 0x0b, 0x12, 0x01, 0xd2,
2092 0xdd, 0x7e, 0xfc, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d,
2093 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00,
2094 0x00, 0x00, 0x06, 0x50, 0x4c, 0x54, 0x45, 0x1b, 0xea, 0x59,
2095 0x0a, 0x0a, 0x0a, 0x0f, 0xba, 0x50, 0x83, 0x00, 0x00, 0x00,
2096 0x0a, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x60, 0x00,
2097 0x00, 0x00, 0x02, 0x00, 0x01, 0xe5, 0x27, 0xde, 0xfc, 0x00,
2098 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60,
2099 0x82];
2100 $data{"snow.png"} =
2101 [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00,
2102 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01,
2103 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x00, 0x00, 0x00, 0x25,
2104 0xdb, 0x56, 0xca, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d,
2105 0x45, 0x07, 0xd2, 0x07, 0x11, 0x0f, 0x1e, 0x1d, 0x75, 0xbc,
2106 0xef, 0x55, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73,
2107 0x00, 0x00, 0x0b, 0x12, 0x00, 0x00, 0x0b, 0x12, 0x01, 0xd2,
2108 0xdd, 0x7e, 0xfc, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d,
2109 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00,
2110 0x00, 0x00, 0x06, 0x50, 0x4c, 0x54, 0x45, 0xff, 0xff, 0xff,
2111 0x00, 0x00, 0x00, 0x55, 0xc2, 0xd3, 0x7e, 0x00, 0x00, 0x00,
2112 0x0a, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x60, 0x00,
2113 0x00, 0x00, 0x02, 0x00, 0x01, 0xe5, 0x27, 0xde, 0xfc, 0x00,
2114 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60,
2115 0x82];
2116 $data{"glass.png"} =
2117 [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00,
2118 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01,
2119 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x00, 0x00, 0x00, 0x25,
2120 0xdb, 0x56, 0xca, 0x00, 0x00, 0x00, 0x04, 0x67, 0x41, 0x4d,
2121 0x41, 0x00, 0x00, 0xb1, 0x8f, 0x0b, 0xfc, 0x61, 0x05, 0x00,
2122 0x00, 0x00, 0x06, 0x50, 0x4c, 0x54, 0x45, 0xff, 0xff, 0xff,
2123 0x00, 0x00, 0x00, 0x55, 0xc2, 0xd3, 0x7e, 0x00, 0x00, 0x00,
2124 0x01, 0x74, 0x52, 0x4e, 0x53, 0x00, 0x40, 0xe6, 0xd8, 0x66,
2125 0x00, 0x00, 0x00, 0x01, 0x62, 0x4b, 0x47, 0x44, 0x00, 0x88,
2126 0x05, 0x1d, 0x48, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59,
2127 0x73, 0x00, 0x00, 0x0b, 0x12, 0x00, 0x00, 0x0b, 0x12, 0x01,
2128 0xd2, 0xdd, 0x7e, 0xfc, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49,
2129 0x4d, 0x45, 0x07, 0xd2, 0x07, 0x13, 0x0f, 0x08, 0x19, 0xc4,
2130 0x40, 0x56, 0x10, 0x00, 0x00, 0x00, 0x0a, 0x49, 0x44, 0x41,
2131 0x54, 0x78, 0x9c, 0x63, 0x60, 0x00, 0x00, 0x00, 0x02, 0x00,
2132 0x01, 0x48, 0xaf, 0xa4, 0x71, 0x00, 0x00, 0x00, 0x00, 0x49,
2133 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82];
2134 $data{"updown.png"} =
2135 [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00,
2136 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x0a,
2137 0x00, 0x00, 0x00, 0x0e, 0x08, 0x06, 0x00, 0x00, 0x00, 0x16,
2138 0xa3, 0x8d, 0xab, 0x00, 0x00, 0x00, 0x3c, 0x49, 0x44, 0x41,
2139 0x54, 0x28, 0xcf, 0x63, 0x60, 0x40, 0x03, 0xff, 0xa1, 0x00,
2140 0x5d, 0x9c, 0x11, 0x5d, 0x11, 0x8a, 0x24, 0x23, 0x23, 0x23,
2141 0x86, 0x42, 0x6c, 0xa6, 0x20, 0x2b, 0x66, 0xc4, 0xa7, 0x08,
2142 0x59, 0x31, 0x23, 0x21, 0x45, 0x30, 0xc0, 0xc4, 0x30, 0x60,
2143 0x80, 0xfa, 0x6e, 0x24, 0x3e, 0x78, 0x48, 0x0a, 0x70, 0x62,
2144 0xa2, 0x90, 0x81, 0xd8, 0x44, 0x01, 0x00, 0xe9, 0x5c, 0x2f,
2145 0xf5, 0xe2, 0x9d, 0x0f, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x49,
2146 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82] if ($sort);
2147 foreach (keys(%data))
2148 {
2149 open(PNG_HANDLE, ">".$_)
2150 or die("ERROR: cannot create $_!\n");
2151 binmode(PNG_HANDLE);
2152 print(PNG_HANDLE map(chr,@{$data{$_}}));
2153 close(PNG_HANDLE);
2154 }
2155 }
2156
2157
2158 #
2159 # write_htaccess_file()
2160 #
2161
2162 sub write_htaccess_file()
2163 {
2164 local *HTACCESS_HANDLE;
2165 my $htaccess_data;
2166
2167 open(*HTACCESS_HANDLE, ">.htaccess")
2168 or die("ERROR: cannot open .htaccess for writing!\n");
2169
2170 $htaccess_data = (<<"END_OF_HTACCESS")
2171 AddEncoding x-gzip .html
2172 END_OF_HTACCESS
2173 ;
2174
2175 print(HTACCESS_HANDLE $htaccess_data);
2176 close(*HTACCESS_HANDLE);
2177 }
2178
2179
2180 #
2181 # write_css_file()
2182 #
2183 # Write the cascading style sheet file gcov.css to the current directory.
2184 # This file defines basic layout attributes of all generated HTML pages.
2185 #
2186
2187 sub write_css_file()
2188 {
2189 local *CSS_HANDLE;
2190
2191 # Check for a specified external style sheet file
2192 if ($css_filename)
2193 {
2194 # Simply copy that file
2195 system("cp", $css_filename, "gcov.css")
2196 and die("ERROR: cannot copy file $css_filename!\n");
2197 return;
2198 }
2199
2200 open(CSS_HANDLE, ">gcov.css")
2201 or die ("ERROR: cannot open gcov.css for writing!\n");
2202
2203
2204 # *************************************************************
2205
2206 my $css_data = ($_=<<"END_OF_CSS")
2207 /* All views: initial background and text color */
2208 body
2209 {
2210 color: #000000;
2211 background-color: #FFFFFF;
2212 }
2213
2214 /* All views: standard link format*/
2215 a:link
2216 {
2217 color: #284FA8;
2218 text-decoration: underline;
2219 }
2220
2221 /* All views: standard link - visited format */
2222 a:visited
2223 {
2224 color: #00CB40;
2225 text-decoration: underline;
2226 }
2227
2228 /* All views: standard link - activated format */
2229 a:active
2230 {
2231 color: #FF0040;
2232 text-decoration: underline;
2233 }
2234
2235 /* All views: main title format */
2236 td.title
2237 {
2238 text-align: center;
2239 padding-bottom: 10px;
2240 font-family: sans-serif;
2241 font-size: 20pt;
2242 font-style: italic;
2243 font-weight: bold;
2244 }
2245
2246 /* All views: header item format */
2247 td.headerItem
2248 {
2249 text-align: right;
2250 padding-right: 6px;
2251 font-family: sans-serif;
2252 font-weight: bold;
2253 vertical-align: top;
2254 white-space: nowrap;
2255 }
2256
2257 /* All views: header item value format */
2258 td.headerValue
2259 {
2260 text-align: left;
2261 color: #284FA8;
2262 font-family: sans-serif;
2263 font-weight: bold;
2264 white-space: nowrap;
2265 }
2266
2267 /* All views: header item coverage table heading */
2268 td.headerCovTableHead
2269 {
2270 text-align: center;
2271 padding-right: 6px;
2272 padding-left: 6px;
2273 padding-bottom: 0px;
2274 font-family: sans-serif;
2275 font-size: 80%;
2276 white-space: nowrap;
2277 }
2278
2279 /* All views: header item coverage table entry */
2280 td.headerCovTableEntry
2281 {
2282 text-align: right;
2283 color: #284FA8;
2284 font-family: sans-serif;
2285 font-weight: bold;
2286 white-space: nowrap;
2287 padding-left: 12px;
2288 padding-right: 4px;
2289 background-color: #DAE7FE;
2290 }
2291
2292 /* All views: header item coverage table entry for high coverage rate */
2293 td.headerCovTableEntryHi
2294 {
2295 text-align: right;
2296 color: #000000;
2297 font-family: sans-serif;
2298 font-weight: bold;
2299 white-space: nowrap;
2300 padding-left: 12px;
2301 padding-right: 4px;
2302 background-color: #A7FC9D;
2303 }
2304
2305 /* All views: header item coverage table entry for medium coverage rate */
2306 td.headerCovTableEntryMed
2307 {
2308 text-align: right;
2309 color: #000000;
2310 font-family: sans-serif;
2311 font-weight: bold;
2312 white-space: nowrap;
2313 padding-left: 12px;
2314 padding-right: 4px;
2315 background-color: #FFEA20;
2316 }
2317
2318 /* All views: header item coverage table entry for ow coverage rate */
2319 td.headerCovTableEntryLo
2320 {
2321 text-align: right;
2322 color: #000000;
2323 font-family: sans-serif;
2324 font-weight: bold;
2325 white-space: nowrap;
2326 padding-left: 12px;
2327 padding-right: 4px;
2328 background-color: #FF0000;
2329 }
2330
2331 /* All views: header legend item for legend entry */
2332 td.headerItemLeg
2333 {
2334 text-align: right;
2335 padding-right: 6px;
2336 font-family: sans-serif;
2337 font-weight: bold;
2338 vertical-align: bottom;
2339 white-space: nowrap;
2340 }
2341
2342 /* All views: header legend value for legend entry */
2343 td.headerValueLeg
2344 {
2345 text-align: left;
2346 color: #000000;
2347 font-family: sans-serif;
2348 font-size: 80%;
2349 white-space: nowrap;
2350 padding-top: 4px;
2351 }
2352
2353 /* All views: color of horizontal ruler */
2354 td.ruler
2355 {
2356 background-color: #6688D4;
2357 }
2358
2359 /* All views: version string format */
2360 td.versionInfo
2361 {
2362 text-align: center;
2363 padding-top: 2px;
2364 font-family: sans-serif;
2365 font-style: italic;
2366 }
2367
2368 /* Directory view/File view (all)/Test case descriptions:
2369 table headline format */
2370 td.tableHead
2371 {
2372 text-align: center;
2373 color: #FFFFFF;
2374 background-color: #6688D4;
2375 font-family: sans-serif;
2376 font-size: 120%;
2377 font-weight: bold;
2378 white-space: nowrap;
2379 padding-left: 4px;
2380 padding-right: 4px;
2381 }
2382
2383 span.tableHeadSort
2384 {
2385 padding-right: 4px;
2386 }
2387
2388 /* Directory view/File view (all): filename entry format */
2389 td.coverFile
2390 {
2391 text-align: left;
2392 padding-left: 10px;
2393 padding-right: 20px;
2394 color: #284FA8;
2395 background-color: #DAE7FE;
2396 font-family: monospace;
2397 }
2398
2399 /* Directory view/File view (all): bar-graph entry format*/
2400 td.coverBar
2401 {
2402 padding-left: 10px;
2403 padding-right: 10px;
2404 background-color: #DAE7FE;
2405 }
2406
2407 /* Directory view/File view (all): bar-graph outline color */
2408 td.coverBarOutline
2409 {
2410 background-color: #000000;
2411 }
2412
2413 /* Directory view/File view (all): percentage entry for files with
2414 high coverage rate */
2415 td.coverPerHi
2416 {
2417 text-align: right;
2418 padding-left: 10px;
2419 padding-right: 10px;
2420 background-color: #A7FC9D;
2421 font-weight: bold;
2422 }
2423
2424 /* Directory view/File view (all): line count entry for files with
2425 high coverage rate */
2426 td.coverNumHi
2427 {
2428 text-align: right;
2429 padding-left: 10px;
2430 padding-right: 10px;
2431 background-color: #A7FC9D;
2432 white-space: nowrap;
2433 }
2434
2435 /* Directory view/File view (all): legend entry for high coverage
2436 rate */
2437 span.coverLegendHi
2438 {
2439 padding-left: 10px;
2440 padding-right: 10px;
2441 padding-bottom: 2px;
2442 background-color: #A7FC9D;
2443 }
2444
2445 /* Directory view/File view (all): percentage entry for files with
2446 medium coverage rate */
2447 td.coverPerMed
2448 {
2449 text-align: right;
2450 padding-left: 10px;
2451 padding-right: 10px;
2452 background-color: #FFEA20;
2453 font-weight: bold;
2454 }
2455
2456 /* Directory view/File view (all): line count entry for files with
2457 medium coverage rate */
2458 td.coverNumMed
2459 {
2460 text-align: right;
2461 padding-left: 10px;
2462 padding-right: 10px;
2463 background-color: #FFEA20;
2464 white-space: nowrap;
2465 }
2466
2467 /* Directory view/File view (all): legend entry for medium coverage
2468 rate */
2469 span.coverLegendMed
2470 {
2471 padding-left: 10px;
2472 padding-right: 10px;
2473 padding-bottom: 2px;
2474 background-color: #FFEA20;
2475 }
2476
2477 /* Directory view/File view (all): percentage entry for files with
2478 low coverage rate */
2479 td.coverPerLo
2480 {
2481 text-align: right;
2482 padding-left: 10px;
2483 padding-right: 10px;
2484 background-color: #FF0000;
2485 font-weight: bold;
2486 }
2487
2488 /* Directory view/File view (all): line count entry for files with
2489 low coverage rate */
2490 td.coverNumLo
2491 {
2492 text-align: right;
2493 padding-left: 10px;
2494 padding-right: 10px;
2495 background-color: #FF0000;
2496 white-space: nowrap;
2497 }
2498
2499 /* Directory view/File view (all): legend entry for low coverage
2500 rate */
2501 span.coverLegendLo
2502 {
2503 padding-left: 10px;
2504 padding-right: 10px;
2505 padding-bottom: 2px;
2506 background-color: #FF0000;
2507 }
2508
2509 /* File view (all): "show/hide details" link format */
2510 a.detail:link
2511 {
2512 color: #B8D0FF;
2513 }
2514
2515 /* File view (all): "show/hide details" link - visited format */
2516 a.detail:visited
2517 {
2518 color: #B8D0FF;
2519 }
2520
2521 /* File view (all): "show/hide details" link - activated format */
2522 a.detail:active
2523 {
2524 color: #FFFFFF;
2525 }
2526
2527 /* File view (detail): test name table headline format */
2528 td.testNameHead
2529 {
2530 text-align: right;
2531 padding-right: 10px;
2532 background-color: #DAE7FE;
2533 font-family: sans-serif;
2534 font-weight: bold;
2535 }
2536
2537 /* File view (detail): test lines table headline format */
2538 td.testLinesHead
2539 {
2540 text-align: center;
2541 background-color: #DAE7FE;
2542 font-family: sans-serif;
2543 font-weight: bold;
2544 }
2545
2546 /* File view (detail): test name entry */
2547 td.testName
2548 {
2549 text-align: right;
2550 padding-right: 10px;
2551 background-color: #DAE7FE;
2552 }
2553
2554 /* File view (detail): test percentage entry */
2555 td.testPer
2556 {
2557 text-align: right;
2558 padding-left: 10px;
2559 padding-right: 10px;
2560 background-color: #DAE7FE;
2561 }
2562
2563 /* File view (detail): test lines count entry */
2564 td.testNum
2565 {
2566 text-align: right;
2567 padding-left: 10px;
2568 padding-right: 10px;
2569 background-color: #DAE7FE;
2570 }
2571
2572 /* Test case descriptions: test name format*/
2573 dt
2574 {
2575 font-family: sans-serif;
2576 font-weight: bold;
2577 }
2578
2579 /* Test case descriptions: description table body */
2580 td.testDescription
2581 {
2582 padding-top: 10px;
2583 padding-left: 30px;
2584 padding-bottom: 10px;
2585 padding-right: 30px;
2586 background-color: #DAE7FE;
2587 }
2588
2589 /* Source code view: function entry */
2590 td.coverFn
2591 {
2592 text-align: left;
2593 padding-left: 10px;
2594 padding-right: 20px;
2595 color: #284FA8;
2596 background-color: #DAE7FE;
2597 font-family: monospace;
2598 }
2599
2600 /* Source code view: function entry zero count*/
2601 td.coverFnLo
2602 {
2603 text-align: right;
2604 padding-left: 10px;
2605 padding-right: 10px;
2606 background-color: #FF0000;
2607 font-weight: bold;
2608 }
2609
2610 /* Source code view: function entry nonzero count*/
2611 td.coverFnHi
2612 {
2613 text-align: right;
2614 padding-left: 10px;
2615 padding-right: 10px;
2616 background-color: #DAE7FE;
2617 font-weight: bold;
2618 }
2619
2620 /* Source code view: source code format */
2621 /* Source code view: source code format */
2622 pre.source
2623 {
2624 font-family: monospace;
2625 white-space: pre;
2626 }
2627
2628 /* Source code view: line number format */
2629 span.lineNum
2630 {
2631 background-color: #EFE383;
2632 }
2633
2634 /* Source code view: format for lines which were executed */
2635 td.lineCov,
2636 span.lineCov
2637 {
2638 background-color: #CAD7FE;
2639 }
2640
2641 /* Source code view: format for Cov legend */
2642 span.coverLegendCov
2643 {
2644 padding-left: 10px;
2645 padding-right: 10px;
2646 padding-bottom: 2px;
2647 background-color: #CAD7FE;
2648 }
2649
2650 /* Source code view: format for lines which were not executed */
2651 td.lineNoCov,
2652 span.lineNoCov
2653 {
2654 background-color: #FF6230;
2655 }
2656
2657 /* Source code view: format for NoCov legend */
2658 span.coverLegendNoCov
2659 {
2660 padding-left: 10px;
2661 padding-right: 10px;
2662 padding-bottom: 2px;
2663 background-color: #FF0000;
2664 }
2665
2666 /* Source code view (function table): standard link - visited format */
2667 td.lineNoCov > a:visited,
2668 td.lineCov > a:visited
2669 {
2670 color: black;
2671 text-decoration: underline;
2672 }
2673
2674 /* Source code view: format for lines which were executed only in a
2675 previous version */
2676 span.lineDiffCov
2677 {
2678 background-color: #B5F7AF;
2679 }
2680
2681 /* Source code view: format for DiffCov legend */
2682 span.LegendDiffCov
2683 {
2684 text-align: center;
2685 padding-left: 10px;
2686 padding-right: 10px;
2687 background-color: #B5F7AF;
2688 }
2689 END_OF_CSS
2690 ;
2691
2692 # *************************************************************
2693
2694
2695 # Remove leading tab from all lines
2696 $css_data =~ s/^\t//gm;
2697
2698 print(CSS_HANDLE $css_data);
2699
2700 close(CSS_HANDLE);
2701 }
2702
2703
2704 #
2705 # get_bar_graph_code(base_dir, cover_found, cover_hit)
2706 #
2707 # Return a string containing HTML code which implements a bar graph display
2708 # for a coverage rate of cover_hit * 100 / cover_found.
2709 #
2710
2711 sub get_bar_graph_code($$$)
2712 {
2713 my $rate;
2714 my $alt;
2715 my $width;
2716 my $remainder;
2717 my $png_name;
2718 my $graph_code;
2719
2720 # Check number of instrumented lines
2721 if ($_[1] == 0) { return ""; }
2722
2723 $rate = $_[2] * 100 / $_[1];
2724 $alt = sprintf("%.1f", $rate)."%";
2725 $width = sprintf("%.0f", $rate);
2726 $remainder = sprintf("%d", 100-$width);
2727
2728 # Decide which .png file to use
2729 $png_name = $rate_png[classify_rate($_[1], $_[2], $med_limit,
2730 $hi_limit)];
2731
2732 if ($width == 0)
2733 {
2734 # Zero coverage
2735 $graph_code = (<<END_OF_HTML)
2736 <table border=0 cellspacing=0 cellpadding=1><tr><td class="cover BarOutline"><img src="$_[0]snow.png" width=100 height=10 alt="$alt"></td></tr></ table>
2737 END_OF_HTML
2738 ;
2739 }
2740 elsif ($width == 100)
2741 {
2742 # Full coverage
2743 $graph_code = (<<END_OF_HTML)
2744 <table border=0 cellspacing=0 cellpadding=1><tr><td class="cover BarOutline"><img src="$_[0]$png_name" width=100 height=10 alt="$alt"></td></tr>< /table>
2745 END_OF_HTML
2746 ;
2747 }
2748 else
2749 {
2750 # Positive coverage
2751 $graph_code = (<<END_OF_HTML)
2752 <table border=0 cellspacing=0 cellpadding=1><tr><td class="cover BarOutline"><img src="$_[0]$png_name" width=$width height=10 alt="$alt"><img src ="$_[0]snow.png" width=$remainder height=10 alt="$alt"></td></tr></table>
2753 END_OF_HTML
2754 ;
2755 }
2756
2757 # Remove leading tabs from all lines
2758 $graph_code =~ s/^\t+//gm;
2759 chomp($graph_code);
2760
2761 return($graph_code);
2762 }
2763
2764 #
2765 # sub classify_rate(found, hit, med_limit, high_limit)
2766 #
2767 # Return 0 for low rate, 1 for medium rate and 2 for hi rate.
2768 #
2769
2770 sub classify_rate($$$$)
2771 {
2772 my ($found, $hit, $med, $hi) = @_;
2773 my $rate;
2774
2775 if ($found == 0) {
2776 return 2;
2777 }
2778 $rate = $hit * 100 / $found;
2779 if ($rate < $med) {
2780 return 0;
2781 } elsif ($rate < $hi) {
2782 return 1;
2783 }
2784 return 2;
2785 }
2786
2787
2788 #
2789 # write_html(filehandle, html_code)
2790 #
2791 # Write out HTML_CODE to FILEHANDLE while removing a leading tabulator mark
2792 # in each line of HTML_CODE.
2793 #
2794
2795 sub write_html(*$)
2796 {
2797 local *HTML_HANDLE = $_[0];
2798 my $html_code = $_[1];
2799
2800 # Remove leading tab from all lines
2801 $html_code =~ s/^\t//gm;
2802
2803 print(HTML_HANDLE $html_code)
2804 or die("ERROR: cannot write HTML data ($!)\n");
2805 }
2806
2807
2808 #
2809 # write_html_prolog(filehandle, base_dir, pagetitle)
2810 #
2811 # Write an HTML prolog common to all HTML files to FILEHANDLE. PAGETITLE will
2812 # be used as HTML page title. BASE_DIR contains a relative path which points
2813 # to the base directory.
2814 #
2815
2816 sub write_html_prolog(*$$)
2817 {
2818 my $basedir = $_[1];
2819 my $pagetitle = $_[2];
2820 my $prolog;
2821
2822 $prolog = $html_prolog;
2823 $prolog =~ s/\@pagetitle\@/$pagetitle/g;
2824 $prolog =~ s/\@basedir\@/$basedir/g;
2825
2826 write_html($_[0], $prolog);
2827 }
2828
2829
2830 #
2831 # write_header_prolog(filehandle, base_dir)
2832 #
2833 # Write beginning of page header HTML code.
2834 #
2835
2836 sub write_header_prolog(*$)
2837 {
2838 # *************************************************************
2839
2840 write_html($_[0], <<END_OF_HTML)
2841 <table width="100%" border=0 cellspacing=0 cellpadding=0>
2842 <tr><td class="title">$title</td></tr>
2843 <tr><td class="ruler"><img src="$_[1]glass.png" width=3 height=3 alt =""></td></tr>
2844
2845 <tr>
2846 <td width="100%">
2847 <table cellpadding=1 border=0 width="100%">
2848 END_OF_HTML
2849 ;
2850
2851 # *************************************************************
2852 }
2853
2854
2855 #
2856 # write_header_line(filehandle, type, additional params..)
2857 #
2858 # Write a header line.
2859 #
2860
2861 sub write_header_line(*$@)
2862 {
2863 my $HANDLE = shift;
2864 my $type = shift;
2865 my @args = @_;
2866
2867 # Reduce indentation by using gotos
2868 if ($type eq 0) {
2869 goto header;
2870 } elsif ($type eq 1) {
2871 goto body;
2872 } elsif ($type eq 2) {
2873 goto legend_dir;
2874 } elsif ($type eq 3) {
2875 goto legend_source;
2876 } elsif ($type eq 4) {
2877 goto half_body;
2878 }
2879
2880 header:
2881 # *************************************************************
2882 write_html($HANDLE, <<END_OF_HTML);
2883 <tr>
2884 <td width="5%"></td>
2885 <td width="10%" class="headerItem">$args[0]</td>
2886 <td width="35%" class="headerValue">$args[1]</td>
2887 <td width="10%"></td>
2888 <td width="10%" class="headerCovTableHead">$args[2]</td>
2889 <td width="10%" class="headerCovTableHead">$args[3]</td>
2890 <td width="15%" class="headerCovTableHead">$args[4]</td>
2891 <td width="5%"></td>
2892 </tr>
2893 END_OF_HTML
2894 # *************************************************************
2895 return;
2896
2897 body:
2898 # *************************************************************
2899 write_html($HANDLE, <<END_OF_HTML);
2900 <tr>
2901 <td></td>
2902 <td class="headerItem">$args[0]</td>
2903 <td class="headerValue">$args[1]</td>
2904 <td class="headerItem">$args[2]</td>
2905 <td class="headerCovTableEntry">$args[3]</td>
2906 <td class="headerCovTableEntry">$args[4]</td>
2907 <td class="headerCovTableEntry$args[5]">$args[6]</td>
2908 </tr>
2909 END_OF_HTML
2910 # *************************************************************
2911 return;
2912
2913 half_body:
2914 # *************************************************************
2915 write_html($HANDLE, <<END_OF_HTML);
2916 <tr>
2917 <td></td>
2918 <td class="headerItem">$args[0]</td>
2919 <td class="headerValue">$args[1]</td>
2920 </tr>
2921 END_OF_HTML
2922 # *************************************************************
2923 return;
2924
2925 legend_dir:
2926 # *************************************************************
2927 write_html($HANDLE, <<END_OF_HTML);
2928 <tr>
2929 <td></td>
2930 <td class="headerItemLeg">$args[0]</td>
2931 <td class="headerValueLeg">
2932 $args[1] </td>
2933 <td></td>
2934 <td class="headerValueLeg" colspan=3>
2935 $args[2] </td>
2936 </tr>
2937 END_OF_HTML
2938 # *************************************************************
2939 return;
2940
2941 legend_source:
2942 # *************************************************************
2943 write_html($HANDLE, <<END_OF_HTML);
2944 <tr>
2945 <td></td>
2946 <td class="headerItem">$args[0]</td>
2947 <td class="headerValueLeg" colspan=5>
2948 <span class="coverLegendNoCov">$args[1]</span>
2949 <span class="coverLegendCov">$args[2]</span>
2950 </td>
2951 </tr>
2952 END_OF_HTML
2953 # *************************************************************
2954 }
2955
2956
2957 #
2958 # write_header_epilog(filehandle, base_dir)
2959 #
2960 # Write end of page header HTML code.
2961 #
2962
2963 sub write_header_epilog(*$)
2964 {
2965 # *************************************************************
2966
2967 write_html($_[0], <<END_OF_HTML)
2968 <tr><td><img src="$_[1]glass.png" width=3 height=3 alt=""></td>< /tr>
2969 </table>
2970 </td>
2971 </tr>
2972 <tr><td class="ruler"><img src="$_[1]glass.png" width=3 height=3 alt =""></td></tr>
2973 </table>
2974
2975 END_OF_HTML
2976 ;
2977
2978 # *************************************************************
2979 }
2980
2981
2982 #
2983 # write_file_table_prolog(filehandle, file_heading, lines_heading, func_heading)
2984 #
2985 # Write heading for file table.
2986 #
2987
2988 sub write_file_table_prolog(*$$$)
2989 {
2990 # *************************************************************
2991
2992 if ($func_coverage)
2993 {
2994 write_html($_[0], <<END_OF_HTML)
2995 <center>
2996 <table width="80%" cellpadding=1 cellspacing=1 border=0>
2997
2998 <tr>
2999 <td width="45%"><br></td>
3000 <td width="15%"></td>
3001 <td width="10%"></td>
3002 <td width="10%"></td>
3003 <td width="10%"></td>
3004 <td width="10%"></td>
3005 </tr>
3006
3007 <tr>
3008 <td class="tableHead">$_[1]</td>
3009 <td class="tableHead" colspan=3>$_[2]</td>
3010 <td class="tableHead" colspan=2>$_[3]</td>
3011 </tr>
3012
3013 END_OF_HTML
3014 ;
3015 }
3016 else
3017 {
3018 write_html($_[0], <<END_OF_HTML)
3019 <center>
3020 <table width="80%" cellpadding=1 cellspacing=1 border=0>
3021
3022 <tr>
3023 <td width="50%"><br></td>
3024 <td width="15%"></td>
3025 <td width="15%"></td>
3026 <td width="20%"></td>
3027 </tr>
3028
3029 <tr>
3030 <td class="tableHead">$_[1]</td>
3031 <td class="tableHead" colspan=3>$_[2]</td>
3032 </tr>
3033
3034 END_OF_HTML
3035 ;
3036 }
3037
3038 # *************************************************************
3039 }
3040
3041
3042 #
3043 # write_file_table_entry(filehandle, cover_filename, cover_bar_graph,
3044 # cover_found, cover_hit, fn_found, fn_hit,
3045 # page_link, func_link)
3046 #
3047 # Write an entry of the file table.
3048 #
3049
3050 sub write_file_table_entry(*$$$$$$$)
3051 {
3052 local *HANDLE = shift;
3053 my ($filename, $bar_graph, $found, $hit, $fn_found, $fn_hit,
3054 $page_link) = @_;
3055 my $rate;
3056 my $rate_string;
3057 my $funcs_string;
3058 my $class_lines = "Lo";
3059 my $class_funcs = "Hi";
3060 my $file_code;
3061
3062 # Add link to source if provided
3063 if (defined($page_link) && $page_link ne "") {
3064 $file_code = "<a href=\"$page_link\">$filename</a>";
3065 } else {
3066 $file_code = $filename;
3067 }
3068
3069 # Get line coverage rate
3070 if ($found > 0)
3071 {
3072 $rate = $hit * 100 / $found;
3073 $rate_string = sprintf("%.1f", $rate)."&nbsp;%";
3074
3075 $class_lines = $rate_name[classify_rate($found, $hit,
3076 $med_limit, $hi_limit)];
3077 }
3078 else
3079 {
3080 $rate_string = "-";
3081 }
3082
3083 # Get function coverage rate
3084 if ($fn_found > 0)
3085 {
3086 $rate = $fn_hit * 100 / $fn_found;
3087 $class_funcs = $rate_name[classify_rate($fn_found, $fn_hit,
3088 $fn_med_limit, $fn_hi_limit)];
3089 $funcs_string = sprintf("%.1f", $rate)."&nbsp;%";
3090 }
3091 else
3092 {
3093 # Define 0 of 0 functions as 100%
3094 $rate = 100;
3095 $funcs_string = "-";
3096 }
3097
3098 # *************************************************************
3099
3100 write_html(*HANDLE, <<END_OF_HTML)
3101 <tr>
3102 <td class="coverFile">$file_code</td>
3103 <td class="coverBar" align="center">
3104 $bar_graph
3105 </td>
3106 <td class="coverPer$class_lines">$rate_string</td>
3107 <td class="coverNum$class_lines">$hit / $found</td>
3108 END_OF_HTML
3109 ;
3110
3111 if ($func_coverage)
3112 {
3113 write_html(*HANDLE, <<END_OF_HTML)
3114 <td class="coverPer$class_funcs">$funcs_string</td>
3115 <td class="coverNum$class_funcs">$fn_hit / $fn_found</td>
3116 END_OF_HTML
3117 ;
3118 }
3119 write_html(*HANDLE, <<END_OF_HTML)
3120 </tr>
3121 END_OF_HTML
3122 ;
3123
3124 # *************************************************************
3125 }
3126
3127
3128 #
3129 # write_file_table_detail_heading(filehandle, left_heading, right_heading)
3130 #
3131 # Write heading for detail section in file table.
3132 #
3133
3134 sub write_file_table_detail_heading(*$$$)
3135 {
3136 my $func_rows = "";
3137
3138 if ($func_coverage)
3139 {
3140 $func_rows = "<td class=\"testLinesHead\" colspan=2>$_[3]</td>";
3141 }
3142
3143 # *************************************************************
3144 write_html($_[0], <<END_OF_HTML)
3145 <tr>
3146 <td class="testNameHead" colspan=2>$_[1]</td>
3147 <td class="testLinesHead" colspan=2>$_[2]</td>
3148 $func_rows
3149 </tr>
3150
3151 END_OF_HTML
3152 ;
3153
3154 # *************************************************************
3155 }
3156
3157
3158 #
3159 # write_file_table_detail_entry(filehandle, test_name,
3160 # cover_found, cover_hit, func_found, func_hit)
3161 #
3162 # Write entry for detail section in file table.
3163 #
3164
3165 sub write_file_table_detail_entry(*$$$$$)
3166 {
3167 my $rate;
3168 my $func_rate;
3169 my $name = $_[1];
3170
3171 if ($_[2]>0)
3172 {
3173 $rate = sprintf("%.1f", $_[3]*100/$_[2])."&nbsp;%";
3174 }
3175 else
3176 {
3177 $rate = "-";
3178 }
3179
3180 if ($_[4]>0)
3181 {
3182 $func_rate = sprintf("%.1f", $_[5]*100/$_[4])."&nbsp;%";
3183 }
3184 else
3185 {
3186 $func_rate = "-";
3187 }
3188
3189 if ($name =~ /^(.*),diff$/)
3190 {
3191 $name = $1." (converted)";
3192 }
3193
3194 if ($name eq "")
3195 {
3196 $name = "<span style=\"font-style:italic\">&lt;unnamed&gt;</span >";
3197 }
3198
3199 # *************************************************************
3200
3201 write_html($_[0], <<END_OF_HTML)
3202 <tr>
3203 <td class="testName" colspan=2>$name</td>
3204 <td class="testPer">$rate</td>
3205 <td class="testNum">$_[3]&nbsp;/&nbsp;$_[2]&nbsp;lines</td>
3206 END_OF_HTML
3207 ;
3208 if ($func_coverage)
3209 {
3210 write_html($_[0], <<END_OF_HTML)
3211 <td class="testPer">$func_rate</td>
3212 <td class="testNum">$_[5]&nbsp;/&nbsp;$_[4]</td>
3213 END_OF_HTML
3214 ;
3215 }
3216 write_html($_[0], <<END_OF_HTML)
3217 </tr>
3218
3219 END_OF_HTML
3220 ;
3221
3222 # *************************************************************
3223 }
3224
3225
3226 #
3227 # write_file_table_epilog(filehandle)
3228 #
3229 # Write end of file table HTML code.
3230 #
3231
3232 sub write_file_table_epilog(*)
3233 {
3234 # *************************************************************
3235
3236 write_html($_[0], <<END_OF_HTML)
3237 </table>
3238 </center>
3239 <br>
3240
3241 END_OF_HTML
3242 ;
3243
3244 # *************************************************************
3245 }
3246
3247
3248 #
3249 # write_test_table_prolog(filehandle, table_heading)
3250 #
3251 # Write heading for test case description table.
3252 #
3253
3254 sub write_test_table_prolog(*$)
3255 {
3256 # *************************************************************
3257
3258 write_html($_[0], <<END_OF_HTML)
3259 <center>
3260 <table width="80%" cellpadding=2 cellspacing=1 border=0>
3261
3262 <tr>
3263 <td><br></td>
3264 </tr>
3265
3266 <tr>
3267 <td class="tableHead">$_[1]</td>
3268 </tr>
3269
3270 <tr>
3271 <td class="testDescription">
3272 <dl>
3273 END_OF_HTML
3274 ;
3275
3276 # *************************************************************
3277 }
3278
3279
3280 #
3281 # write_test_table_entry(filehandle, test_name, test_description)
3282 #
3283 # Write entry for the test table.
3284 #
3285
3286 sub write_test_table_entry(*$$)
3287 {
3288 # *************************************************************
3289
3290 write_html($_[0], <<END_OF_HTML)
3291 <dt>$_[1]<a name="$_[1]">&nbsp;</a></dt>
3292 <dd>$_[2]<br><br></dd>
3293 END_OF_HTML
3294 ;
3295
3296 # *************************************************************
3297 }
3298
3299
3300 #
3301 # write_test_table_epilog(filehandle)
3302 #
3303 # Write end of test description table HTML code.
3304 #
3305
3306 sub write_test_table_epilog(*)
3307 {
3308 # *************************************************************
3309
3310 write_html($_[0], <<END_OF_HTML)
3311 </dl>
3312 </td>
3313 </tr>
3314 </table>
3315 </center>
3316 <br>
3317
3318 END_OF_HTML
3319 ;
3320
3321 # *************************************************************
3322 }
3323
3324
3325 #
3326 # write_source_prolog(filehandle)
3327 #
3328 # Write start of source code table.
3329 #
3330
3331 sub write_source_prolog(*)
3332 {
3333 # *************************************************************
3334
3335 write_html($_[0], <<END_OF_HTML)
3336 <table cellpadding=0 cellspacing=0 border=0>
3337 <tr>
3338 <td><br></td>
3339 </tr>
3340 <tr>
3341 <td><pre class="source">
3342 END_OF_HTML
3343 ;
3344
3345 # *************************************************************
3346 }
3347
3348
3349 #
3350 # write_source_line(filehandle, line_num, source, hit_count, converted,
3351 # add_anchor)
3352 #
3353 # Write formatted source code line. Return a line in a format as needed
3354 # by gen_png()
3355 #
3356
3357 sub write_source_line(*$$$$$)
3358 {
3359 my $source_format;
3360 my $count;
3361 my $result;
3362 my $anchor_start = "";
3363 my $anchor_end = "";
3364
3365 if (!(defined$_[3]))
3366 {
3367 $result = "";
3368 $source_format = "";
3369 $count = " "x15;
3370 }
3371 elsif ($_[3] == 0)
3372 {
3373 $result = $_[3];
3374 $source_format = '<span class="lineNoCov">';
3375 $count = sprintf("%15d", $_[3]);
3376 }
3377 elsif ($_[4] && defined($highlight))
3378 {
3379 $result = "*".$_[3];
3380 $source_format = '<span class="lineDiffCov">';
3381 $count = sprintf("%15d", $_[3]);
3382 }
3383 else
3384 {
3385 $result = $_[3];
3386 $source_format = '<span class="lineCov">';
3387 $count = sprintf("%15d", $_[3]);
3388 }
3389
3390 $result .= ":".$_[2];
3391
3392 # Write out a line number navigation anchor every $nav_resolution
3393 # lines if necessary
3394 if ($_[5])
3395 {
3396 $anchor_start = "<a name=\"$_[1]\">";
3397 $anchor_end = "</a>";
3398 }
3399
3400
3401 # *************************************************************
3402
3403 write_html($_[0],
3404 $anchor_start.
3405 '<span class="lineNum">'.sprintf("%8d", $_[1]).
3406 " </span>$source_format$count : ".
3407 escape_html($_[2]).($source_format?"</span>":"").
3408 $anchor_end."\n");
3409
3410 # *************************************************************
3411
3412 return($result);
3413 }
3414
3415
3416 #
3417 # write_source_epilog(filehandle)
3418 #
3419 # Write end of source code table.
3420 #
3421
3422 sub write_source_epilog(*)
3423 {
3424 # *************************************************************
3425
3426 write_html($_[0], <<END_OF_HTML)
3427 </pre>
3428 </td>
3429 </tr>
3430 </table>
3431 <br>
3432
3433 END_OF_HTML
3434 ;
3435
3436 # *************************************************************
3437 }
3438
3439
3440 #
3441 # write_html_epilog(filehandle, base_dir[, break_frames])
3442 #
3443 # Write HTML page footer to FILEHANDLE. BREAK_FRAMES should be set when
3444 # this page is embedded in a frameset, clicking the URL link will then
3445 # break this frameset.
3446 #
3447
3448 sub write_html_epilog(*$;$)
3449 {
3450 my $basedir = $_[1];
3451 my $break_code = "";
3452 my $epilog;
3453
3454 if (defined($_[2]))
3455 {
3456 $break_code = " target=\"_parent\"";
3457 }
3458
3459 # *************************************************************
3460
3461 write_html($_[0], <<END_OF_HTML)
3462 <table width="100%" border=0 cellspacing=0 cellpadding=0>
3463 <tr><td class="ruler"><img src="$_[1]glass.png" width=3 height=3 alt=" "></td></tr>
3464 <tr><td class="versionInfo">Generated by: <a href="$lcov_url"$break_co de>$lcov_version</a></td></tr>
3465 </table>
3466 <br>
3467 END_OF_HTML
3468 ;
3469
3470 $epilog = $html_epilog;
3471 $epilog =~ s/\@basedir\@/$basedir/g;
3472
3473 write_html($_[0], $epilog);
3474 }
3475
3476
3477 #
3478 # write_frameset(filehandle, basedir, basename, pagetitle)
3479 #
3480 #
3481
3482 sub write_frameset(*$$$)
3483 {
3484 my $frame_width = $overview_width + 40;
3485
3486 # *************************************************************
3487
3488 write_html($_[0], <<END_OF_HTML)
3489 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN">
3490
3491 <html lang="en">
3492
3493 <head>
3494 <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1 ">
3495 <title>$_[3]</title>
3496 <link rel="stylesheet" type="text/css" href="$_[1]gcov.css">
3497 </head>
3498
3499 <frameset cols="$frame_width,*">
3500 <frame src="$_[2].gcov.overview.$html_ext" name="overview">
3501 <frame src="$_[2].gcov.$html_ext" name="source">
3502 <noframes>
3503 <center>Frames not supported by your browser!<br></center>
3504 </noframes>
3505 </frameset>
3506
3507 </html>
3508 END_OF_HTML
3509 ;
3510
3511 # *************************************************************
3512 }
3513
3514
3515 #
3516 # sub write_overview_line(filehandle, basename, line, link)
3517 #
3518 #
3519
3520 sub write_overview_line(*$$$)
3521 {
3522 my $y1 = $_[2] - 1;
3523 my $y2 = $y1 + $nav_resolution - 1;
3524 my $x2 = $overview_width - 1;
3525
3526 # *************************************************************
3527
3528 write_html($_[0], <<END_OF_HTML)
3529 <area shape="rect" coords="0,$y1,$x2,$y2" href="$_[1].gcov.$html_ext #$_[3]" target="source" alt="overview">
3530 END_OF_HTML
3531 ;
3532
3533 # *************************************************************
3534 }
3535
3536
3537 #
3538 # write_overview(filehandle, basedir, basename, pagetitle, lines)
3539 #
3540 #
3541
3542 sub write_overview(*$$$$)
3543 {
3544 my $index;
3545 my $max_line = $_[4] - 1;
3546 my $offset;
3547
3548 # *************************************************************
3549
3550 write_html($_[0], <<END_OF_HTML)
3551 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
3552
3553 <html lang="en">
3554
3555 <head>
3556 <title>$_[3]</title>
3557 <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1 ">
3558 <link rel="stylesheet" type="text/css" href="$_[1]gcov.css">
3559 </head>
3560
3561 <body>
3562 <map name="overview">
3563 END_OF_HTML
3564 ;
3565
3566 # *************************************************************
3567
3568 # Make $offset the next higher multiple of $nav_resolution
3569 $offset = ($nav_offset + $nav_resolution - 1) / $nav_resolution;
3570 $offset = sprintf("%d", $offset ) * $nav_resolution;
3571
3572 # Create image map for overview image
3573 for ($index = 1; $index <= $_[4]; $index += $nav_resolution)
3574 {
3575 # Enforce nav_offset
3576 if ($index < $offset + 1)
3577 {
3578 write_overview_line($_[0], $_[2], $index, 1);
3579 }
3580 else
3581 {
3582 write_overview_line($_[0], $_[2], $index, $index - $offs et);
3583 }
3584 }
3585
3586 # *************************************************************
3587
3588 write_html($_[0], <<END_OF_HTML)
3589 </map>
3590
3591 <center>
3592 <a href="$_[2].gcov.$html_ext#top" target="source">Top</a><br><br>
3593 <img src="$_[2].gcov.png" width=$overview_width height=$max_line alt=" Overview" border=0 usemap="#overview">
3594 </center>
3595 </body>
3596 </html>
3597 END_OF_HTML
3598 ;
3599
3600 # *************************************************************
3601 }
3602
3603
3604 # rate_to_col(found, hit)
3605 #
3606 # Return Lo, Med or Hi, depending on the coverage rate.
3607 #
3608
3609 sub rate_to_col($$)
3610 {
3611 my ($found, $hit) = @_;
3612 my $rate;
3613
3614 if ($found == 0) {
3615 return "Hi";
3616 }
3617 $rate = 100 * $hit / $found;
3618 if ($rate < $med_limit) {
3619 return "Lo";
3620 } elsif ($rate < $hi_limit) {
3621 return "Med";
3622 }
3623 return "Hi";
3624 }
3625
3626 # format_rate(found, hit)
3627 #
3628 # Return formatted percent string for coverage rate.
3629 #
3630
3631 sub format_rate($$)
3632 {
3633 return $_[0] == 0 ? "-" : sprintf("%.1f", $_[1] * 100 / $_[0])." %";
3634 }
3635
3636 sub get_legend_code($$$)
3637 {
3638 my ($text, $med, $hi) = @_;
3639 my $result;
3640
3641 $result = <<EOF;
3642 $text<br>
3643 <span class="coverLegendLo">0% to $med%</span>
3644 <span class="coverLegendMed">$med% to $hi%</span>
3645 <span class="coverLegendHi">$hi% to 100%</span>
3646 EOF
3647 return $result;
3648 }
3649
3650 #
3651 # write_header(filehandle, type, trunc_file_name, rel_file_name, lines_found,
3652 # lines_hit, funcs_found, funcs_hit, sort_type)
3653 #
3654 # Write a complete standard page header. TYPE may be (0, 1, 2, 3, 4)
3655 # corresponding to (directory view header, file view header, source view
3656 # header, test case description header, function view header)
3657 #
3658
3659 sub write_header(*$$$$$$$$)
3660 {
3661 local *HTML_HANDLE = $_[0];
3662 my $type = $_[1];
3663 my $trunc_name = $_[2];
3664 my $rel_filename = $_[3];
3665 my $lines_found = $_[4];
3666 my $lines_hit = $_[5];
3667 my $fn_found = $_[6];
3668 my $fn_hit = $_[7];
3669 my $sort_type = $_[8];
3670 my $base_dir;
3671 my $view;
3672 my $test;
3673 my $base_name;
3674
3675 $base_name = basename($rel_filename);
3676
3677 # Prepare text for "current view" field
3678 if ($type == 0)
3679 {
3680 # Main overview
3681 $base_dir = "";
3682 $view = $overview_title;
3683 }
3684 elsif ($type == 1)
3685 {
3686 # Directory overview
3687 $base_dir = get_relative_base_path($rel_filename);
3688 $view = "<a href=\"$base_dir"."index.$html_ext\">".
3689 "$overview_title</a> - $trunc_name";
3690 }
3691 elsif ($type == 2 || $type == 4)
3692 {
3693 # File view
3694 my $dir_name = dirname($rel_filename);
3695
3696 $base_dir = get_relative_base_path($dir_name);
3697 if ($frames)
3698 {
3699 # Need to break frameset when clicking any of these
3700 # links
3701 $view = "<a href=\"$base_dir"."index.$html_ext\" ".
3702 "target=\"_parent\">$overview_title</a> - ".
3703 "<a href=\"index.$html_ext\" target=\"_parent\"> ".
3704 "$dir_name</a> - $base_name";
3705 }
3706 else
3707 {
3708 $view = "<a href=\"$base_dir"."index.$html_ext\">".
3709 "$overview_title</a> - ".
3710 "<a href=\"index.$html_ext\">".
3711 "$dir_name</a> - $base_name";
3712 }
3713
3714 # Add function suffix
3715 if ($func_coverage) {
3716 if ($type == 2) {
3717 $view .= " (source / <a href=\"$base_name.func.$ html_ext\">functions</a>)";
3718 } elsif ($type == 4) {
3719 $view .= " (<a href=\"$base_name.gcov.$html_ext\ ">source</a> / functions)";
3720 }
3721 }
3722 }
3723 elsif ($type == 3)
3724 {
3725 # Test description header
3726 $base_dir = "";
3727 $view = "<a href=\"$base_dir"."index.$html_ext\">".
3728 "$overview_title</a> - test case descriptions";
3729 }
3730
3731 # Prepare text for "test" field
3732 $test = escape_html($test_title);
3733
3734 # Append link to test description page if available
3735 if (%test_description && ($type != 3))
3736 {
3737 if ($frames && ($type == 2 || $type == 4))
3738 {
3739 # Need to break frameset when clicking this link
3740 $test .= " ( <a href=\"$base_dir".
3741 "descriptions.$html_ext\" target=\"_parent\">".
3742 "view descriptions</a> )";
3743 }
3744 else
3745 {
3746 $test .= " ( <a href=\"$base_dir".
3747 "descriptions.$html_ext\">".
3748 "view descriptions</a> )";
3749 }
3750 }
3751
3752 # Write header
3753 write_header_prolog(*HTML_HANDLE, $base_dir);
3754 write_header_line(*HTML_HANDLE, 0, "Current view:", $view,
3755 "Found", "Hit", "Coverage");
3756 write_header_line(*HTML_HANDLE, 1, "Test:", $test, "Lines:",
3757 $lines_found, $lines_hit,
3758 $rate_name[classify_rate($lines_found, $lines_hit,
3759 $med_limit, $hi_limit)],
3760 format_rate($lines_found, $lines_hit));
3761 if ($func_coverage) {
3762 write_header_line(*HTML_HANDLE, 1, "Date:", $date, "Functions:",
3763 $fn_found, $fn_hit,
3764 $rate_name[classify_rate($fn_found,
3765 $fn_hit,
3766 $fn_med_limit,
3767 $fn_hi_limit)],
3768 format_rate($fn_found, $fn_hit));
3769 } else {
3770 write_header_line(*HTML_HANDLE, 4, "Date:", $date);
3771 }
3772 if ($legend) {
3773 if ($type == 0 || $type == 1) {
3774 my $line_code = get_legend_code("Line coverage:",
3775 $med_limit, $hi_limit);
3776 my $func_code = "";
3777
3778 if ($func_coverage) {
3779 $func_code = get_legend_code(
3780 "Function coverage:",
3781 $fn_med_limit,
3782 $fn_hi_limit);
3783 }
3784 write_header_line(*HTML_HANDLE, 2, "Colors:",
3785 $line_code, $func_code);
3786 } elsif ($type == 2 || $type == 4) {
3787 write_header_line(*HTML_HANDLE, 3, "Colors:",
3788 "not hit", "hit");
3789 }
3790 }
3791 write_header_epilog(*HTML_HANDLE, $base_dir);
3792 }
3793
3794
3795 #
3796 # split_filename(filename)
3797 #
3798 # Return (path, filename, extension) for a given FILENAME.
3799 #
3800
3801 sub split_filename($)
3802 {
3803 if (!$_[0]) { return(); }
3804 my @path_components = split('/', $_[0]);
3805 my @file_components = split('\.', pop(@path_components));
3806 my $extension = pop(@file_components);
3807
3808 return (join("/",@path_components), join(".",@file_components),
3809 $extension);
3810 }
3811
3812 #
3813 # get_sorted_keys(hash_ref, sort_type)
3814 #
3815
3816 sub get_sorted_keys($$)
3817 {
3818 my ($hash, $type) = @_;
3819
3820 if ($type == 0) {
3821 # Sort by name
3822 return sort(keys(%{$hash}));
3823 } elsif ($type == 1) {
3824 # Sort by line coverage
3825 return sort({$hash->{$a}[5] <=> $hash->{$b}[5]} keys(%{$hash}));
3826 } elsif ($type == 2) {
3827 # Sort by function coverage;
3828 return sort({$hash->{$a}[6] <=> $hash->{$b}[6]} keys(%{$hash}));
3829 }
3830 }
3831
3832 sub get_sort_code($$$)
3833 {
3834 my ($link, $alt, $base) = @_;
3835 my $png;
3836 my $link_start;
3837 my $link_end;
3838
3839 if (!defined($link)) {
3840 $png = "glass.png";
3841 $link_start = "";
3842 $link_end = "";
3843 } else {
3844 $png = "updown.png";
3845 $link_start = '<a href="'.$link.'">';
3846 $link_end = "</a>";
3847 }
3848
3849 return ' <span class="tableHeadSort">'.$link_start.
3850 '<img src="'.$base.$png.'" width=10 height=14 '.
3851 'alt="'.$alt.'" title="'.$alt.'" border=0>'.$link_end.'</span>';
3852 }
3853
3854 sub get_file_code($$$$)
3855 {
3856 my ($type, $text, $sort_button, $base) = @_;
3857 my $result = $text;
3858 my $link;
3859
3860 if ($sort_button) {
3861 if ($type == 1) {
3862 $link = "index.$html_ext";
3863 } else {
3864 $link = "index-detail.$html_ext";
3865 }
3866 }
3867 $result .= get_sort_code($link, "Sort by name", $base);
3868
3869 return $result;
3870 }
3871
3872 sub get_line_code($$$$$)
3873 {
3874 my ($type, $sort_type, $text, $sort_button, $base) = @_;
3875 my $result = $text;
3876 my $sort_link;
3877
3878 if ($type == 1) {
3879 # Just text
3880 if ($sort_button) {
3881 $sort_link = "index-sort-l.$html_ext";
3882 }
3883 } elsif ($type == 2) {
3884 # Text + link to detail view
3885 $result .= ' ( <a class="detail" href="index-detail'.
3886 $fileview_sortname[$sort_type].'.'.$html_ext.
3887 '">show details</a> )';
3888 if ($sort_button) {
3889 $sort_link = "index-sort-l.$html_ext";
3890 }
3891 } else {
3892 # Text + link to standard view
3893 $result .= ' ( <a class="detail" href="index'.
3894 $fileview_sortname[$sort_type].'.'.$html_ext.
3895 '">hide details</a> )';
3896 if ($sort_button) {
3897 $sort_link = "index-detail-sort-l.$html_ext";
3898 }
3899 }
3900 # Add sort button
3901 $result .= get_sort_code($sort_link, "Sort by line coverage", $base);
3902
3903 return $result;
3904 }
3905
3906 sub get_func_code($$$$)
3907 {
3908 my ($type, $text, $sort_button, $base) = @_;
3909 my $result = $text;
3910 my $link;
3911
3912 if ($sort_button) {
3913 if ($type == 1) {
3914 $link = "index-sort-f.$html_ext";
3915 } else {
3916 $link = "index-detail-sort-f.$html_ext";
3917 }
3918 }
3919 $result .= get_sort_code($link, "Sort by function coverage", $base);
3920 return $result;
3921 }
3922
3923 #
3924 # write_file_table(filehandle, base_dir, overview, testhash, testfnchash,
3925 # fileview, sort_type)
3926 #
3927 # Write a complete file table. OVERVIEW is a reference to a hash containing
3928 # the following mapping:
3929 #
3930 # filename -> "lines_found,lines_hit,funcs_found,funcs_hit,page_link,
3931 # func_link"
3932 #
3933 # TESTHASH is a reference to the following hash:
3934 #
3935 # filename -> \%testdata
3936 # %testdata: name of test affecting this file -> \%testcount
3937 # %testcount: line number -> execution count for a single test
3938 #
3939 # Heading of first column is "Filename" if FILEVIEW is true, "Directory name"
3940 # otherwise.
3941 #
3942
3943 sub write_file_table(*$$$$$$)
3944 {
3945 local *HTML_HANDLE = $_[0];
3946 my $base_dir = $_[1];
3947 my $overview = $_[2];
3948 my $testhash = $_[3];
3949 my $testfnchash = $_[4];
3950 my $fileview = $_[5];
3951 my $sort_type = $_[6];
3952 my $filename;
3953 my $bar_graph;
3954 my $hit;
3955 my $found;
3956 my $fn_found;
3957 my $fn_hit;
3958 my $page_link;
3959 my $testname;
3960 my $testdata;
3961 my $testfncdata;
3962 my $testcount;
3963 my $testfnccount;
3964 my %affecting_tests;
3965 my $line_code = "";
3966 my $func_code;
3967 my $file_code;
3968
3969 # Determine HTML code for column headings
3970 if (($base_dir ne "") && $show_details)
3971 {
3972 my $detailed = keys(%{$testhash});
3973
3974 $file_code = get_file_code($detailed ? 2 : 1,
3975 $fileview ? "Filename" : "Directory",
3976 $sort && $sort_type != 0, $base_dir);
3977 $line_code = get_line_code($detailed ? 3 : 2, $sort_type,
3978 "Line Coverage",
3979 $sort && $sort_type != 1, $base_dir);
3980 $func_code = get_func_code($detailed ? 2 : 1, "Functions",
3981 $sort && $sort_type != 2, $base_dir);
3982 } else {
3983 $file_code = get_file_code(1,
3984 $fileview ? "Filename" : "Directory",
3985 $sort && $sort_type != 0, $base_dir);
3986 $line_code = get_line_code(1, $sort_type, "Line Coverage",
3987 $sort && $sort_type != 1, $base_dir);
3988 $func_code = get_func_code(1, "Functions",
3989 $sort && $sort_type != 2, $base_dir);
3990 }
3991
3992 write_file_table_prolog(*HTML_HANDLE, $file_code, $line_code,
3993 $func_code);
3994
3995 foreach $filename (get_sorted_keys($overview, $sort_type))
3996 {
3997 ($found, $hit, $fn_found, $fn_hit, $page_link)
3998 = @{$overview->{$filename}};
3999 $bar_graph = get_bar_graph_code($base_dir, $found, $hit);
4000
4001 $testdata = $testhash->{$filename};
4002 $testfncdata = $testfnchash->{$filename};
4003
4004 write_file_table_entry(*HTML_HANDLE, $filename, $bar_graph,
4005 $found, $hit, $fn_found, $fn_hit,
4006 $page_link);
4007
4008 # Check whether we should write test specific coverage
4009 # as well
4010 if (!($show_details && $testdata)) { next; }
4011
4012 # Filter out those tests that actually affect this file
4013 %affecting_tests = %{ get_affecting_tests($testdata,
4014 $testfncdata) };
4015
4016 # Does any of the tests affect this file at all?
4017 if (!%affecting_tests) { next; }
4018
4019 # Write test details for this entry
4020 write_file_table_detail_heading(*HTML_HANDLE, "Test name",
4021 "Lines hit", "Functions hit");
4022
4023 foreach $testname (keys(%affecting_tests))
4024 {
4025 ($found, $hit, $fn_found, $fn_hit) =
4026 split(",", $affecting_tests{$testname});
4027
4028 # Insert link to description of available
4029 if ($test_description{$testname})
4030 {
4031 $testname = "<a href=\"$base_dir".
4032 "descriptions.$html_ext#$testname\"> ".
4033 "$testname</a>";
4034 }
4035
4036 write_file_table_detail_entry(*HTML_HANDLE, $testname,
4037 $found, $hit, $fn_found, $fn_hit);
4038 }
4039 }
4040
4041 write_file_table_epilog(*HTML_HANDLE);
4042 }
4043
4044
4045 #
4046 # get_found_and_hit(hash)
4047 #
4048 # Return the count for entries (found) and entries with an execution count
4049 # greater than zero (hit) in a hash (linenumber -> execution count) as
4050 # a list (found, hit)
4051 #
4052
4053 sub get_found_and_hit($)
4054 {
4055 my %hash = %{$_[0]};
4056 my $found = 0;
4057 my $hit = 0;
4058
4059 # Calculate sum
4060 $found = 0;
4061 $hit = 0;
4062
4063 foreach (keys(%hash))
4064 {
4065 $found++;
4066 if ($hash{$_}>0) { $hit++; }
4067 }
4068
4069 return ($found, $hit);
4070 }
4071
4072
4073 #
4074 # get_func_found_and_hit(sumfnccount)
4075 #
4076 # Return (f_found, f_hit) for sumfnccount
4077 #
4078
4079 sub get_func_found_and_hit($)
4080 {
4081 my ($sumfnccount) = @_;
4082 my $function;
4083 my $fn_found;
4084 my $fn_hit;
4085
4086 $fn_found = scalar(keys(%{$sumfnccount}));
4087 $fn_hit = 0;
4088 foreach $function (keys(%{$sumfnccount})) {
4089 if ($sumfnccount->{$function} > 0) {
4090 $fn_hit++;
4091 }
4092 }
4093 return ($fn_found, $fn_hit);
4094 }
4095
4096
4097 #
4098 # get_affecting_tests(testdata, testfncdata)
4099 #
4100 # HASHREF contains a mapping filename -> (linenumber -> exec count). Return
4101 # a hash containing mapping filename -> "lines found, lines hit" for each
4102 # filename which has a nonzero hit count.
4103 #
4104
4105 sub get_affecting_tests($$)
4106 {
4107 my $testdata = $_[0];
4108 my $testfncdata = $_[1];
4109 my $testname;
4110 my $testcount;
4111 my $testfnccount;
4112 my %result;
4113 my $found;
4114 my $hit;
4115 my $fn_found;
4116 my $fn_hit;
4117
4118 foreach $testname (keys(%{$testdata}))
4119 {
4120 # Get (line number -> count) hash for this test case
4121 $testcount = $testdata->{$testname};
4122 $testfnccount = $testfncdata->{$testname};
4123
4124 # Calculate sum
4125 ($found, $hit) = get_found_and_hit($testcount);
4126 ($fn_found, $fn_hit) = get_func_found_and_hit($testfnccount);
4127
4128 if ($hit>0)
4129 {
4130 $result{$testname} = "$found,$hit,$fn_found,$fn_hit";
4131 }
4132 }
4133
4134 return(\%result);
4135 }
4136
4137
4138 sub get_hash_reverse($)
4139 {
4140 my ($hash) = @_;
4141 my %result;
4142
4143 foreach (keys(%{$hash})) {
4144 $result{$hash->{$_}} = $_;
4145 }
4146
4147 return \%result;
4148 }
4149
4150 #
4151 # write_source(filehandle, source_filename, count_data, checksum_data,
4152 # converted_data, func_data)
4153 #
4154 # Write an HTML view of a source code file. Returns a list containing
4155 # data as needed by gen_png().
4156 #
4157 # Die on error.
4158 #
4159
4160 sub write_source($$$$$$)
4161 {
4162 local *HTML_HANDLE = $_[0];
4163 local *SOURCE_HANDLE;
4164 my $source_filename = $_[1];
4165 my %count_data;
4166 my $line_number;
4167 my @result;
4168 my $checkdata = $_[3];
4169 my $converted = $_[4];
4170 my $funcdata = $_[5];
4171 my $datafunc = get_hash_reverse($funcdata);
4172 my $add_anchor;
4173
4174 if ($_[2])
4175 {
4176 %count_data = %{$_[2]};
4177 }
4178
4179 open(SOURCE_HANDLE, "<".$source_filename)
4180 or die("ERROR: cannot open $source_filename for reading!\n");
4181
4182 write_source_prolog(*HTML_HANDLE);
4183
4184 for ($line_number = 1; <SOURCE_HANDLE> ; $line_number++)
4185 {
4186 chomp($_);
4187
4188 # Source code matches coverage data?
4189 if (defined($checkdata->{$line_number}) &&
4190 ($checkdata->{$line_number} ne md5_base64($_)))
4191 {
4192 die("ERROR: checksum mismatch at $source_filename:".
4193 "$line_number\n");
4194 }
4195
4196 $add_anchor = 0;
4197 if ($frames) {
4198 if (($line_number - 1) % $nav_resolution == 0) {
4199 $add_anchor = 1;
4200 }
4201 }
4202 if ($func_coverage) {
4203 if ($line_number == 1) {
4204 $add_anchor = 1;
4205 } elsif (defined($datafunc->{$line_number +
4206 $func_offset})) {
4207 $add_anchor = 1;
4208 }
4209 }
4210 push (@result,
4211 write_source_line(HTML_HANDLE, $line_number,
4212 $_, $count_data{$line_number},
4213 $converted->{$line_number},
4214 $add_anchor));
4215 }
4216
4217 close(SOURCE_HANDLE);
4218 write_source_epilog(*HTML_HANDLE);
4219 return(@result);
4220 }
4221
4222
4223 sub funcview_get_func_code($$$)
4224 {
4225 my ($name, $base, $type) = @_;
4226 my $result;
4227 my $link;
4228
4229 if ($sort && $type == 1) {
4230 $link = "$name.func.$html_ext";
4231 }
4232 $result = "Function Name";
4233 $result .= get_sort_code($link, "Sort by function name", $base);
4234
4235 return $result;
4236 }
4237
4238 sub funcview_get_count_code($$$)
4239 {
4240 my ($name, $base, $type) = @_;
4241 my $result;
4242 my $link;
4243
4244 if ($sort && $type == 0) {
4245 $link = "$name.func-sort-c.$html_ext";
4246 }
4247 $result = "Hit count";
4248 $result .= get_sort_code($link, "Sort by hit count", $base);
4249
4250 return $result;
4251 }
4252
4253 #
4254 # funcview_get_sorted(funcdata, sumfncdata, sort_type)
4255 #
4256 # Depending on the value of sort_type, return a list of functions sorted
4257 # by name (type 0) or by the associated call count (type 1).
4258 #
4259
4260 sub funcview_get_sorted($$$)
4261 {
4262 my ($funcdata, $sumfncdata, $type) = @_;
4263
4264 if ($type == 0) {
4265 return sort(keys(%{$funcdata}));
4266 }
4267 return sort({$sumfncdata->{$b} <=> $sumfncdata->{$a}}
4268 keys(%{$sumfncdata}));
4269 }
4270
4271 #
4272 # write_function_table(filehandle, source_file, sumcount, funcdata,
4273 # sumfnccount, testfncdata)
4274 #
4275 # Write an HTML table listing all functions in a source file, including
4276 # also function call counts and line coverages inside of each function.
4277 #
4278 # Die on error.
4279 #
4280
4281 sub write_function_table(*$$$$$$$$)
4282 {
4283 local *HTML_HANDLE = $_[0];
4284 my $source = $_[1];
4285 my $sumcount = $_[2];
4286 my $funcdata = $_[3];
4287 my $sumfncdata = $_[4];
4288 my $testfncdata = $_[5];
4289 my $name = $_[6];
4290 my $base = $_[7];
4291 my $type = $_[8];
4292 my $func;
4293 my $func_code;
4294 my $count_code;
4295
4296 # Get HTML code for headings
4297 $func_code = funcview_get_func_code($name, $base, $type);
4298 $count_code = funcview_get_count_code($name, $base, $type);
4299 write_html(*HTML_HANDLE, <<END_OF_HTML)
4300 <center>
4301 <table width="60%" cellpadding=1 cellspacing=1 border=0>
4302 <tr><td><br></td></tr>
4303 <tr>
4304 <td width="80%" class="tableHead">$func_code</td>
4305 <td width="20%" class="tableHead">$count_code</td>
4306 </tr>
4307 END_OF_HTML
4308 ;
4309
4310 # Get a sorted table
4311 foreach $func (funcview_get_sorted($funcdata, $sumfncdata, $type)) {
4312 my $startline = $funcdata->{$func} - $func_offset;
4313 my $name = escape_html($func);
4314 my $count = $sumfncdata->{$name};
4315 my $countstyle;
4316
4317 if ($startline < 1) {
4318 $startline = 1;
4319 }
4320 if ($count == 0) {
4321 $countstyle = "coverFnLo";
4322 } else {
4323 $countstyle = "coverFnHi";
4324 }
4325
4326 write_html(*HTML_HANDLE, <<END_OF_HTML)
4327 <tr>
4328 <td class="coverFn"><a href="$source#$startline">$name</a></td>
4329 <td class="$countstyle">$count</td>
4330 </tr>
4331 END_OF_HTML
4332 ;
4333 }
4334 write_html(*HTML_HANDLE, <<END_OF_HTML)
4335 </table>
4336 <br>
4337 </center>
4338 END_OF_HTML
4339 ;
4340 }
4341
4342
4343 #
4344 # info(printf_parameter)
4345 #
4346 # Use printf to write PRINTF_PARAMETER to stdout only when the $quiet flag
4347 # is not set.
4348 #
4349
4350 sub info(@)
4351 {
4352 if (!$quiet)
4353 {
4354 # Print info string
4355 printf(@_);
4356 }
4357 }
4358
4359
4360 #
4361 # subtract_counts(data_ref, base_ref)
4362 #
4363
4364 sub subtract_counts($$)
4365 {
4366 my %data = %{$_[0]};
4367 my %base = %{$_[1]};
4368 my $line;
4369 my $data_count;
4370 my $base_count;
4371 my $hit = 0;
4372 my $found = 0;
4373
4374 foreach $line (keys(%data))
4375 {
4376 $found++;
4377 $data_count = $data{$line};
4378 $base_count = $base{$line};
4379
4380 if (defined($base_count))
4381 {
4382 $data_count -= $base_count;
4383
4384 # Make sure we don't get negative numbers
4385 if ($data_count<0) { $data_count = 0; }
4386 }
4387
4388 $data{$line} = $data_count;
4389 if ($data_count > 0) { $hit++; }
4390 }
4391
4392 return (\%data, $found, $hit);
4393 }
4394
4395
4396 #
4397 # subtract_fnccounts(data, base)
4398 #
4399 # Subtract function call counts found in base from those in data.
4400 # Return (data, f_found, f_hit).
4401 #
4402
4403 sub subtract_fnccounts($$)
4404 {
4405 my %data = %{$_[0]};
4406 my %base = %{$_[1]};
4407 my $func;
4408 my $data_count;
4409 my $base_count;
4410 my $fn_hit = 0;
4411 my $fn_found = 0;
4412
4413 foreach $func (keys(%data)) {
4414 $fn_found++;
4415 $data_count = $data{$func};
4416 $base_count = $base{$func};
4417
4418 if (defined($base_count)) {
4419 $data_count -= $base_count;
4420
4421 # Make sure we don't get negative numbers
4422 if ($data_count < 0) {
4423 $data_count = 0;
4424 }
4425 }
4426
4427 $data{$func} = $data_count;
4428 if ($data_count > 0) {
4429 $fn_hit++;
4430 }
4431 }
4432
4433 return (\%data, $fn_found, $fn_hit);
4434 }
4435
4436
4437 #
4438 # apply_baseline(data_ref, baseline_ref)
4439 #
4440 # Subtract the execution counts found in the baseline hash referenced by
4441 # BASELINE_REF from actual data in DATA_REF.
4442 #
4443
4444 sub apply_baseline($$)
4445 {
4446 my %data_hash = %{$_[0]};
4447 my %base_hash = %{$_[1]};
4448 my $filename;
4449 my $testname;
4450 my $data;
4451 my $data_testdata;
4452 my $data_funcdata;
4453 my $data_checkdata;
4454 my $data_testfncdata;
4455 my $data_count;
4456 my $data_testfnccount;
4457 my $base;
4458 my $base_checkdata;
4459 my $base_sumfnccount;
4460 my $base_count;
4461 my $sumcount;
4462 my $sumfnccount;
4463 my $found;
4464 my $hit;
4465 my $fn_found;
4466 my $fn_hit;
4467
4468 foreach $filename (keys(%data_hash))
4469 {
4470 # Get data set for data and baseline
4471 $data = $data_hash{$filename};
4472 $base = $base_hash{$filename};
4473
4474 # Skip data entries for which no base entry exists
4475 if (!defined($base))
4476 {
4477 next;
4478 }
4479
4480 # Get set entries for data and baseline
4481 ($data_testdata, undef, $data_funcdata, $data_checkdata,
4482 $data_testfncdata) = get_info_entry($data);
4483 (undef, $base_count, undef, $base_checkdata, undef,
4484 $base_sumfnccount) = get_info_entry($base);
4485
4486 # Check for compatible checksums
4487 merge_checksums($data_checkdata, $base_checkdata, $filename);
4488
4489 # sumcount has to be calculated anew
4490 $sumcount = {};
4491 $sumfnccount = {};
4492
4493 # For each test case, subtract test specific counts
4494 foreach $testname (keys(%{$data_testdata}))
4495 {
4496 # Get counts of both data and baseline
4497 $data_count = $data_testdata->{$testname};
4498 $data_testfnccount = $data_testfncdata->{$testname};
4499
4500 ($data_count, undef, $hit) =
4501 subtract_counts($data_count, $base_count);
4502 ($data_testfnccount) =
4503 subtract_fnccounts($data_testfnccount,
4504 $base_sumfnccount);
4505
4506 # Check whether this test case did hit any line at all
4507 if ($hit > 0)
4508 {
4509 # Write back resulting hash
4510 $data_testdata->{$testname} = $data_count;
4511 $data_testfncdata->{$testname} =
4512 $data_testfnccount;
4513 }
4514 else
4515 {
4516 # Delete test case which did not impact this
4517 # file
4518 delete($data_testdata->{$testname});
4519 delete($data_testfncdata->{$testname});
4520 }
4521
4522 # Add counts to sum of counts
4523 ($sumcount, $found, $hit) =
4524 add_counts($sumcount, $data_count);
4525 ($sumfnccount, $fn_found, $fn_hit) =
4526 add_fnccounts($sumfnccount, $data_testfnccount);
4527 }
4528
4529 # Write back resulting entry
4530 set_info_entry($data, $data_testdata, $sumcount, $data_funcdata,
4531 $data_checkdata, $data_testfncdata, $sumfnccount,
4532 $found, $hit, $fn_found, $fn_hit);
4533
4534 $data_hash{$filename} = $data;
4535 }
4536
4537 return (\%data_hash);
4538 }
4539
4540
4541 #
4542 # remove_unused_descriptions()
4543 #
4544 # Removes all test descriptions from the global hash %test_description which
4545 # are not present in %info_data.
4546 #
4547
4548 sub remove_unused_descriptions()
4549 {
4550 my $filename; # The current filename
4551 my %test_list; # Hash containing found test names
4552 my $test_data; # Reference to hash test_name -> count_data
4553 my $before; # Initial number of descriptions
4554 my $after; # Remaining number of descriptions
4555
4556 $before = scalar(keys(%test_description));
4557
4558 foreach $filename (keys(%info_data))
4559 {
4560 ($test_data) = get_info_entry($info_data{$filename});
4561 foreach (keys(%{$test_data}))
4562 {
4563 $test_list{$_} = "";
4564 }
4565 }
4566
4567 # Remove descriptions for tests which are not in our list
4568 foreach (keys(%test_description))
4569 {
4570 if (!defined($test_list{$_}))
4571 {
4572 delete($test_description{$_});
4573 }
4574 }
4575
4576 $after = scalar(keys(%test_description));
4577 if ($after < $before)
4578 {
4579 info("Removed ".($before - $after).
4580 " unused descriptions, $after remaining.\n");
4581 }
4582 }
4583
4584
4585 #
4586 # apply_prefix(filename, prefix)
4587 #
4588 # If FILENAME begins with PREFIX, remove PREFIX from FILENAME and return
4589 # resulting string, otherwise return FILENAME.
4590 #
4591
4592 sub apply_prefix($$)
4593 {
4594 my $filename = $_[0];
4595 my $prefix = $_[1];
4596
4597 if (defined($prefix) && ($prefix ne ""))
4598 {
4599 if ($filename =~ /^\Q$prefix\E\/(.*)$/)
4600 {
4601 return substr($filename, length($prefix) + 1);
4602 }
4603 }
4604
4605 return $filename;
4606 }
4607
4608
4609 #
4610 # system_no_output(mode, parameters)
4611 #
4612 # Call an external program using PARAMETERS while suppressing depending on
4613 # the value of MODE:
4614 #
4615 # MODE & 1: suppress STDOUT
4616 # MODE & 2: suppress STDERR
4617 #
4618 # Return 0 on success, non-zero otherwise.
4619 #
4620
4621 sub system_no_output($@)
4622 {
4623 my $mode = shift;
4624 my $result;
4625 local *OLD_STDERR;
4626 local *OLD_STDOUT;
4627
4628 # Save old stdout and stderr handles
4629 ($mode & 1) && open(OLD_STDOUT, ">>&STDOUT");
4630 ($mode & 2) && open(OLD_STDERR, ">>&STDERR");
4631
4632 # Redirect to /dev/null
4633 ($mode & 1) && open(STDOUT, ">/dev/null");
4634 ($mode & 2) && open(STDERR, ">/dev/null");
4635
4636 system(@_);
4637 $result = $?;
4638
4639 # Close redirected handles
4640 ($mode & 1) && close(STDOUT);
4641 ($mode & 2) && close(STDERR);
4642
4643 # Restore old handles
4644 ($mode & 1) && open(STDOUT, ">>&OLD_STDOUT");
4645 ($mode & 2) && open(STDERR, ">>&OLD_STDERR");
4646
4647 return $result;
4648 }
4649
4650
4651 #
4652 # read_config(filename)
4653 #
4654 # Read configuration file FILENAME and return a reference to a hash containing
4655 # all valid key=value pairs found.
4656 #
4657
4658 sub read_config($)
4659 {
4660 my $filename = $_[0];
4661 my %result;
4662 my $key;
4663 my $value;
4664 local *HANDLE;
4665
4666 if (!open(HANDLE, "<$filename"))
4667 {
4668 warn("WARNING: cannot read configuration file $filename\n");
4669 return undef;
4670 }
4671 while (<HANDLE>)
4672 {
4673 chomp;
4674 # Skip comments
4675 s/#.*//;
4676 # Remove leading blanks
4677 s/^\s+//;
4678 # Remove trailing blanks
4679 s/\s+$//;
4680 next unless length;
4681 ($key, $value) = split(/\s*=\s*/, $_, 2);
4682 if (defined($key) && defined($value))
4683 {
4684 $result{$key} = $value;
4685 }
4686 else
4687 {
4688 warn("WARNING: malformed statement in line $. ".
4689 "of configuration file $filename\n");
4690 }
4691 }
4692 close(HANDLE);
4693 return \%result;
4694 }
4695
4696
4697 #
4698 # apply_config(REF)
4699 #
4700 # REF is a reference to a hash containing the following mapping:
4701 #
4702 # key_string => var_ref
4703 #
4704 # where KEY_STRING is a keyword and VAR_REF is a reference to an associated
4705 # variable. If the global configuration hash CONFIG contains a value for
4706 # keyword KEY_STRING, VAR_REF will be assigned the value for that keyword.
4707 #
4708
4709 sub apply_config($)
4710 {
4711 my $ref = $_[0];
4712
4713 foreach (keys(%{$ref}))
4714 {
4715 if (defined($config->{$_}))
4716 {
4717 ${$ref->{$_}} = $config->{$_};
4718 }
4719 }
4720 }
4721
4722
4723 #
4724 # get_html_prolog(FILENAME)
4725 #
4726 # If FILENAME is defined, return contents of file. Otherwise return default
4727 # HTML prolog. Die on error.
4728 #
4729
4730 sub get_html_prolog($)
4731 {
4732 my $filename = $_[0];
4733 my $result = "";
4734
4735 if (defined($filename))
4736 {
4737 local *HANDLE;
4738
4739 open(HANDLE, "<".$filename)
4740 or die("ERROR: cannot open html prolog $filename!\n");
4741 while (<HANDLE>)
4742 {
4743 $result .= $_;
4744 }
4745 close(HANDLE);
4746 }
4747 else
4748 {
4749 $result = <<END_OF_HTML
4750 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
4751
4752 <html lang="en">
4753
4754 <head>
4755 <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
4756 <title>\@pagetitle\@</title>
4757 <link rel="stylesheet" type="text/css" href="\@basedir\@gcov.css">
4758 </head>
4759
4760 <body>
4761
4762 END_OF_HTML
4763 ;
4764 }
4765
4766 return $result;
4767 }
4768
4769
4770 #
4771 # get_html_epilog(FILENAME)
4772 #
4773 # If FILENAME is defined, return contents of file. Otherwise return default
4774 # HTML epilog. Die on error.
4775 #
4776 sub get_html_epilog($)
4777 {
4778 my $filename = $_[0];
4779 my $result = "";
4780
4781 if (defined($filename))
4782 {
4783 local *HANDLE;
4784
4785 open(HANDLE, "<".$filename)
4786 or die("ERROR: cannot open html epilog $filename!\n");
4787 while (<HANDLE>)
4788 {
4789 $result .= $_;
4790 }
4791 close(HANDLE);
4792 }
4793 else
4794 {
4795 $result = <<END_OF_HTML
4796
4797 </body>
4798 </html>
4799 END_OF_HTML
4800 ;
4801 }
4802
4803 return $result;
4804
4805 }
4806
4807 sub warn_handler($)
4808 {
4809 my ($msg) = @_;
4810
4811 warn("$tool_name: $msg");
4812 }
4813
4814 sub die_handler($)
4815 {
4816 my ($msg) = @_;
4817
4818 die("$tool_name: $msg");
4819 }
OLDNEW
« no previous file with comments | « third_party/lcov/bin/gendesc ('k') | third_party/lcov/bin/geninfo » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698