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

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

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/genhtml ('k') | third_party/lcov/bin/genpng » ('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,2007
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 # geninfo
21 #
22 # This script generates .info files from data files as created by code
23 # instrumented with gcc's built-in profiling mechanism. Call it with
24 # --help and refer to the geninfo man page to get information on usage
25 # and available options.
26 #
27 #
28 # Authors:
29 # 2002-08-23 created by Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com>
30 # IBM Lab Boeblingen
31 # based on code by Manoj Iyer <manjo@mail.utexas.edu> and
32 # Megan Bock <mbock@us.ibm.com>
33 # IBM Austin
34 # 2002-09-05 / Peter Oberparleiter: implemented option that allows file list
35 # 2003-04-16 / Peter Oberparleiter: modified read_gcov so that it can also
36 # parse the new gcov format which is to be introduced in gcc 3.3
37 # 2003-04-30 / Peter Oberparleiter: made info write to STDERR, not STDOUT
38 # 2003-07-03 / Peter Oberparleiter: added line checksum support, added
39 # --no-checksum
40 # 2003-09-18 / Nigel Hinds: capture branch coverage data from GCOV
41 # 2003-12-11 / Laurent Deniel: added --follow option
42 # workaround gcov (<= 3.2.x) bug with empty .da files
43 # 2004-01-03 / Laurent Deniel: Ignore empty .bb files
44 # 2004-02-16 / Andreas Krebbel: Added support for .gcno/.gcda files and
45 # gcov versioning
46 # 2004-08-09 / Peter Oberparleiter: added configuration file support
47 # 2008-07-14 / Tom Zoerner: added --function-coverage command line option
48 # 2008-08-13 / Peter Oberparleiter: modified function coverage
49 # implementation (now enabled per default)
50 #
51
52 use strict;
53 use File::Basename;
54 use Getopt::Long;
55 use Digest::MD5 qw(md5_base64);
56
57
58 # Constants
59 our $lcov_version = "LCOV version 1.7";
60 our $lcov_url = "http://ltp.sourceforge.net/coverage/lcov.php";
61 our $gcov_tool = "gcov";
62 our $tool_name = basename($0);
63
64 our $GCOV_VERSION_3_4_0 = 0x30400;
65 our $GCOV_VERSION_3_3_0 = 0x30300;
66 our $GCNO_FUNCTION_TAG = 0x01000000;
67 our $GCNO_LINES_TAG = 0x01450000;
68 our $GCNO_FILE_MAGIC = 0x67636e6f;
69 our $BBG_FILE_MAGIC = 0x67626267;
70
71 our $COMPAT_HAMMER = "hammer";
72
73 our $ERROR_GCOV = 0;
74 our $ERROR_SOURCE = 1;
75
76 # Prototypes
77 sub print_usage(*);
78 sub gen_info($);
79 sub process_dafile($);
80 sub match_filename($@);
81 sub solve_ambiguous_match($$$);
82 sub split_filename($);
83 sub solve_relative_path($$);
84 sub get_dir($);
85 sub read_gcov_header($);
86 sub read_gcov_file($);
87 sub read_bb_file($$);
88 sub read_string(*$);
89 sub read_gcno_file($$);
90 sub read_gcno_string(*$);
91 sub read_hammer_bbg_file($$);
92 sub read_hammer_bbg_string(*$);
93 sub unpack_int32($$);
94 sub info(@);
95 sub get_gcov_version();
96 sub system_no_output($@);
97 sub read_config($);
98 sub apply_config($);
99 sub gen_initial_info($);
100 sub process_graphfile($);
101 sub warn_handler($);
102 sub die_handler($);
103
104 # Global variables
105 our $gcov_version;
106 our $graph_file_extension;
107 our $data_file_extension;
108 our @data_directory;
109 our $test_name = "";
110 our $quiet;
111 our $help;
112 our $output_filename;
113 our $base_directory;
114 our $version;
115 our $follow;
116 our $checksum;
117 our $no_checksum;
118 our $preserve_paths;
119 our $compat_libtool;
120 our $no_compat_libtool;
121 our $adjust_testname;
122 our $config; # Configuration file contents
123 our $compatibility; # Compatibility version flag - used to indicate
124 # non-standard GCOV data format versions
125 our @ignore_errors; # List of errors to ignore (parameter)
126 our @ignore; # List of errors to ignore (array)
127 our $initial;
128 our $no_recursion = 0;
129 our $maxdepth;
130
131 our $cwd = `pwd`;
132 chomp($cwd);
133
134
135 #
136 # Code entry point
137 #
138
139 # Register handler routine to be called when interrupted
140 $SIG{"INT"} = \&int_handler;
141 $SIG{__WARN__} = \&warn_handler;
142 $SIG{__DIE__} = \&die_handler;
143
144 # Read configuration file if available
145 if (-r $ENV{"HOME"}."/.lcovrc")
146 {
147 $config = read_config($ENV{"HOME"}."/.lcovrc");
148 }
149 elsif (-r "/etc/lcovrc")
150 {
151 $config = read_config("/etc/lcovrc");
152 }
153
154 if ($config)
155 {
156 # Copy configuration file values to variables
157 apply_config({
158 "geninfo_gcov_tool" => \$gcov_tool,
159 "geninfo_adjust_testname" => \$adjust_testname,
160 "geninfo_checksum" => \$checksum,
161 "geninfo_no_checksum" => \$no_checksum, # deprecated
162 "geninfo_compat_libtool" => \$compat_libtool});
163
164 # Merge options
165 if (defined($no_checksum))
166 {
167 $checksum = ($no_checksum ? 0 : 1);
168 $no_checksum = undef;
169 }
170 }
171
172 # Parse command line options
173 if (!GetOptions("test-name=s" => \$test_name,
174 "output-filename=s" => \$output_filename,
175 "checksum" => \$checksum,
176 "no-checksum" => \$no_checksum,
177 "base-directory=s" => \$base_directory,
178 "version" =>\$version,
179 "quiet" => \$quiet,
180 "help|?" => \$help,
181 "follow" => \$follow,
182 "compat-libtool" => \$compat_libtool,
183 "no-compat-libtool" => \$no_compat_libtool,
184 "gcov-tool=s" => \$gcov_tool,
185 "ignore-errors=s" => \@ignore_errors,
186 "initial|i" => \$initial,
187 "no-recursion" => \$no_recursion,
188 ))
189 {
190 print(STDERR "Use $tool_name --help to get usage information\n");
191 exit(1);
192 }
193 else
194 {
195 # Merge options
196 if (defined($no_checksum))
197 {
198 $checksum = ($no_checksum ? 0 : 1);
199 $no_checksum = undef;
200 }
201
202 if (defined($no_compat_libtool))
203 {
204 $compat_libtool = ($no_compat_libtool ? 0 : 1);
205 $no_compat_libtool = undef;
206 }
207 }
208
209 @data_directory = @ARGV;
210
211 # Check for help option
212 if ($help)
213 {
214 print_usage(*STDOUT);
215 exit(0);
216 }
217
218 # Check for version option
219 if ($version)
220 {
221 print("$tool_name: $lcov_version\n");
222 exit(0);
223 }
224
225 # Make sure test names only contain valid characters
226 if ($test_name =~ s/\W/_/g)
227 {
228 warn("WARNING: invalid characters removed from testname!\n");
229 }
230
231 # Adjust test name to include uname output if requested
232 if ($adjust_testname)
233 {
234 $test_name .= "__".`uname -a`;
235 $test_name =~ s/\W/_/g;
236 }
237
238 # Make sure base_directory contains an absolute path specification
239 if ($base_directory)
240 {
241 $base_directory = solve_relative_path($cwd, $base_directory);
242 }
243
244 # Check for follow option
245 if ($follow)
246 {
247 $follow = "-follow"
248 }
249 else
250 {
251 $follow = "";
252 }
253
254 # Determine checksum mode
255 if (defined($checksum))
256 {
257 # Normalize to boolean
258 $checksum = ($checksum ? 1 : 0);
259 }
260 else
261 {
262 # Default is off
263 $checksum = 0;
264 }
265
266 # Determine libtool compatibility mode
267 if (defined($compat_libtool))
268 {
269 $compat_libtool = ($compat_libtool? 1 : 0);
270 }
271 else
272 {
273 # Default is on
274 $compat_libtool = 1;
275 }
276
277 # Determine max depth for recursion
278 if ($no_recursion)
279 {
280 $maxdepth = "-maxdepth 1";
281 }
282 else
283 {
284 $maxdepth = "";
285 }
286
287 # Check for directory name
288 if (!@data_directory)
289 {
290 die("No directory specified\n".
291 "Use $tool_name --help to get usage information\n");
292 }
293 else
294 {
295 foreach (@data_directory)
296 {
297 stat($_);
298 if (!-r _)
299 {
300 die("ERROR: cannot read $_!\n");
301 }
302 }
303 }
304
305 if (@ignore_errors)
306 {
307 my @expanded;
308 my $error;
309
310 # Expand comma-separated entries
311 foreach (@ignore_errors) {
312 if (/,/)
313 {
314 push(@expanded, split(",", $_));
315 }
316 else
317 {
318 push(@expanded, $_);
319 }
320 }
321
322 foreach (@expanded)
323 {
324 /^gcov$/ && do { $ignore[$ERROR_GCOV] = 1; next; } ;
325 /^source$/ && do { $ignore[$ERROR_SOURCE] = 1; next; };
326 die("ERROR: unknown argument for --ignore-errors: $_\n");
327 }
328 }
329
330 if (system_no_output(3, $gcov_tool, "--help") == -1)
331 {
332 die("ERROR: need tool $gcov_tool!\n");
333 }
334
335 $gcov_version = get_gcov_version();
336
337 if ($gcov_version < $GCOV_VERSION_3_4_0)
338 {
339 if (defined($compatibility) && $compatibility eq $COMPAT_HAMMER)
340 {
341 $data_file_extension = ".da";
342 $graph_file_extension = ".bbg";
343 }
344 else
345 {
346 $data_file_extension = ".da";
347 $graph_file_extension = ".bb";
348 }
349 }
350 else
351 {
352 $data_file_extension = ".gcda";
353 $graph_file_extension = ".gcno";
354 }
355
356 # Check for availability of --preserve-paths option of gcov
357 if (`$gcov_tool --help` =~ /--preserve-paths/)
358 {
359 $preserve_paths = "--preserve-paths";
360 }
361
362 # Check output filename
363 if (defined($output_filename) && ($output_filename ne "-"))
364 {
365 # Initially create output filename, data is appended
366 # for each data file processed
367 local *DUMMY_HANDLE;
368 open(DUMMY_HANDLE, ">$output_filename")
369 or die("ERROR: cannot create $output_filename!\n");
370 close(DUMMY_HANDLE);
371
372 # Make $output_filename an absolute path because we're going
373 # to change directories while processing files
374 if (!($output_filename =~ /^\/(.*)$/))
375 {
376 $output_filename = $cwd."/".$output_filename;
377 }
378 }
379
380 # Do something
381 if ($initial)
382 {
383 foreach (@data_directory)
384 {
385 gen_initial_info($_);
386 }
387 }
388 else
389 {
390 foreach (@data_directory)
391 {
392 gen_info($_);
393 }
394 }
395 info("Finished .info-file creation\n");
396
397 exit(0);
398
399
400
401 #
402 # print_usage(handle)
403 #
404 # Print usage information.
405 #
406
407 sub print_usage(*)
408 {
409 local *HANDLE = $_[0];
410
411 print(HANDLE <<END_OF_USAGE);
412 Usage: $tool_name [OPTIONS] DIRECTORY
413
414 Traverse DIRECTORY and create a .info file for each data file found. Note
415 that you may specify more than one directory, all of which are then processed
416 sequentially.
417
418 -h, --help Print this help, then exit
419 -v, --version Print version number, then exit
420 -q, --quiet Do not print progress messages
421 -i, --initial Capture initial zero coverage data
422 -t, --test-name NAME Use test case name NAME for resulting data
423 -o, --output-filename OUTFILE Write data only to OUTFILE
424 -f, --follow Follow links when searching .da/.gcda files
425 -b, --base-directory DIR Use DIR as base directory for relative paths
426 --(no-)checksum Enable (disable) line checksumming
427 --(no-)compat-libtool Enable (disable) libtool compatibility mode
428 --gcov-tool TOOL Specify gcov tool location
429 --ignore-errors ERROR Continue after ERROR (gcov, source)
430 --no-recursion Exlude subdirectories from processing
431 --function-coverage Capture function call counts
432
433 For more information see: $lcov_url
434 END_OF_USAGE
435 ;
436 }
437
438
439 #
440 # gen_info(directory)
441 #
442 # Traverse DIRECTORY and create a .info file for each data file found.
443 # The .info file contains TEST_NAME in the following format:
444 #
445 # TN:<test name>
446 #
447 # For each source file name referenced in the data file, there is a section
448 # containing source code and coverage data:
449 #
450 # SF:<absolute path to the source file>
451 # FN:<line number of function start>,<function name> for each function
452 # DA:<line number>,<execution count> for each instrumented line
453 # LH:<number of lines with an execution count> greater than 0
454 # LF:<number of instrumented lines>
455 #
456 # Sections are separated by:
457 #
458 # end_of_record
459 #
460 # In addition to the main source code file there are sections for each
461 # #included file containing executable code. Note that the absolute path
462 # of a source file is generated by interpreting the contents of the respective
463 # graph file. Relative filenames are prefixed with the directory in which the
464 # graph file is found. Note also that symbolic links to the graph file will be
465 # resolved so that the actual file path is used instead of the path to a link.
466 # This approach is necessary for the mechanism to work with the /proc/gcov
467 # files.
468 #
469 # Die on error.
470 #
471
472 sub gen_info($)
473 {
474 my $directory = $_[0];
475 my @file_list;
476
477 if (-d $directory)
478 {
479 info("Scanning $directory for $data_file_extension ".
480 "files ...\n");
481
482 @file_list = `find "$directory" $maxdepth $follow -name \\*$data _file_extension -type f 2>/dev/null`;
483 chomp(@file_list);
484 @file_list or die("ERROR: no $data_file_extension files found ".
485 "in $directory!\n");
486 info("Found %d data files in %s\n", $#file_list+1, $directory);
487 }
488 else
489 {
490 @file_list = ($directory);
491 }
492
493 # Process all files in list
494 foreach (@file_list) { process_dafile($_); }
495 }
496
497
498 #
499 # process_dafile(da_filename)
500 #
501 # Create a .info file for a single data file.
502 #
503 # Die on error.
504 #
505
506 sub process_dafile($)
507 {
508 info("Processing %s\n", $_[0]);
509
510 my $da_filename; # Name of data file to process
511 my $da_dir; # Directory of data file
512 my $source_dir; # Directory of source file
513 my $da_basename; # data filename without ".da/.gcda" extension
514 my $bb_filename; # Name of respective graph file
515 my %bb_content; # Contents of graph file
516 my $gcov_error; # Error code of gcov tool
517 my $object_dir; # Directory containing all object files
518 my $source_filename; # Name of a source code file
519 my $gcov_file; # Name of a .gcov file
520 my @gcov_content; # Content of a .gcov file
521 my @gcov_branches; # Branch content of a .gcov file
522 my @gcov_functions; # Function calls of a .gcov file
523 my @gcov_list; # List of generated .gcov files
524 my $line_number; # Line number count
525 my $lines_hit; # Number of instrumented lines hit
526 my $lines_found; # Number of instrumented lines found
527 my $funcs_hit; # Number of instrumented functions hit
528 my $funcs_found; # Number of instrumented functions found
529 my $source; # gcov source header information
530 my $object; # gcov object header information
531 my @matches; # List of absolute paths matching filename
532 my @unprocessed; # List of unprocessed source code files
533 my $base_dir; # Base directory for current file
534 my @result;
535 my $index;
536 my $da_renamed; # If data file is to be renamed
537 local *INFO_HANDLE;
538
539 # Get path to data file in absolute and normalized form (begins with /,
540 # contains no more ../ or ./)
541 $da_filename = solve_relative_path($cwd, $_[0]);
542
543 # Get directory and basename of data file
544 ($da_dir, $da_basename) = split_filename($da_filename);
545
546 # avoid files from .libs dirs
547 if ($compat_libtool && $da_dir =~ m/(.*)\/\.libs$/) {
548 $source_dir = $1;
549 } else {
550 $source_dir = $da_dir;
551 }
552
553 if (-z $da_filename)
554 {
555 $da_renamed = 1;
556 }
557 else
558 {
559 $da_renamed = 0;
560 }
561
562 # Construct base_dir for current file
563 if ($base_directory)
564 {
565 $base_dir = $base_directory;
566 }
567 else
568 {
569 $base_dir = $source_dir;
570 }
571
572 # Check for writable $base_dir (gcov will try to write files there)
573 stat($base_dir);
574 if (!-w _)
575 {
576 die("ERROR: cannot write to directory $base_dir!\n");
577 }
578
579 # Construct name of graph file
580 $bb_filename = $da_dir."/".$da_basename.$graph_file_extension;
581
582 # Find out the real location of graph file in case we're just looking at
583 # a link
584 while (readlink($bb_filename))
585 {
586 my $last_dir = dirname($bb_filename);
587
588 $bb_filename = readlink($bb_filename);
589 $bb_filename = solve_relative_path($last_dir, $bb_filename);
590 }
591
592 # Ignore empty graph file (e.g. source file with no statement)
593 if (-z $bb_filename)
594 {
595 warn("WARNING: empty $bb_filename (skipped)\n");
596 return;
597 }
598
599 # Read contents of graph file into hash. We need it later to find out
600 # the absolute path to each .gcov file created as well as for
601 # information about functions and their source code positions.
602 if ($gcov_version < $GCOV_VERSION_3_4_0)
603 {
604 if (defined($compatibility) && $compatibility eq $COMPAT_HAMMER)
605 {
606 %bb_content = read_hammer_bbg_file($bb_filename,
607 $base_dir);
608 }
609 else
610 {
611 %bb_content = read_bb_file($bb_filename, $base_dir);
612 }
613 }
614 else
615 {
616 %bb_content = read_gcno_file($bb_filename, $base_dir);
617 }
618
619 # Set $object_dir to real location of object files. This may differ
620 # from $da_dir if the graph file is just a link to the "real" object
621 # file location.
622 $object_dir = dirname($bb_filename);
623
624 # Is the data file in a different directory? (this happens e.g. with
625 # the gcov-kernel patch)
626 if ($object_dir ne $da_dir)
627 {
628 # Need to create link to data file in $object_dir
629 system("ln", "-s", $da_filename,
630 "$object_dir/$da_basename$data_file_extension")
631 and die ("ERROR: cannot create link $object_dir/".
632 "$da_basename$data_file_extension!\n");
633 }
634
635 # Change to directory containing data files and apply GCOV
636 chdir($base_dir);
637
638 if ($da_renamed)
639 {
640 # Need to rename empty data file to workaround
641 # gcov <= 3.2.x bug (Abort)
642 system_no_output(3, "mv", "$da_filename", "$da_filename.ori")
643 and die ("ERROR: cannot rename $da_filename\n");
644 }
645
646 # Execute gcov command and suppress standard output
647 if ($preserve_paths)
648 {
649 $gcov_error = system_no_output(1, $gcov_tool, $da_filename,
650 "-o", $object_dir,
651 "--preserve-paths",
652 "-b");
653 }
654 else
655 {
656 $gcov_error = system_no_output(1, $gcov_tool, $da_filename,
657 "-o", $object_dir,
658 "-b");
659 }
660
661 if ($da_renamed)
662 {
663 system_no_output(3, "mv", "$da_filename.ori", "$da_filename")
664 and die ("ERROR: cannot rename $da_filename.ori");
665 }
666
667 # Clean up link
668 if ($object_dir ne $da_dir)
669 {
670 unlink($object_dir."/".$da_basename.$data_file_extension);
671 }
672
673 if ($gcov_error)
674 {
675 if ($ignore[$ERROR_GCOV])
676 {
677 warn("WARNING: GCOV failed for $da_filename!\n");
678 return;
679 }
680 die("ERROR: GCOV failed for $da_filename!\n");
681 }
682
683 # Collect data from resulting .gcov files and create .info file
684 @gcov_list = glob("*.gcov");
685
686 # Check for files
687 if (!@gcov_list)
688 {
689 warn("WARNING: gcov did not create any files for ".
690 "$da_filename!\n");
691 }
692
693 # Check whether we're writing to a single file
694 if ($output_filename)
695 {
696 if ($output_filename eq "-")
697 {
698 *INFO_HANDLE = *STDOUT;
699 }
700 else
701 {
702 # Append to output file
703 open(INFO_HANDLE, ">>$output_filename")
704 or die("ERROR: cannot write to ".
705 "$output_filename!\n");
706 }
707 }
708 else
709 {
710 # Open .info file for output
711 open(INFO_HANDLE, ">$da_filename.info")
712 or die("ERROR: cannot create $da_filename.info!\n");
713 }
714
715 # Write test name
716 printf(INFO_HANDLE "TN:%s\n", $test_name);
717
718 # Traverse the list of generated .gcov files and combine them into a
719 # single .info file
720 @unprocessed = keys(%bb_content);
721 foreach $gcov_file (@gcov_list)
722 {
723 ($source, $object) = read_gcov_header($gcov_file);
724
725 if (defined($source))
726 {
727 $source = solve_relative_path($base_dir, $source);
728 }
729
730 # gcov will happily create output even if there's no source code
731 # available - this interferes with checksum creation so we need
732 # to pull the emergency brake here.
733 if (defined($source) && ! -r $source && $checksum)
734 {
735 if ($ignore[$ERROR_SOURCE])
736 {
737 warn("WARNING: could not read source file ".
738 "$source\n");
739 next;
740 }
741 die("ERROR: could not read source file $source\n");
742 }
743
744 @matches = match_filename(defined($source) ? $source :
745 $gcov_file, keys(%bb_content));
746
747 # Skip files that are not mentioned in the graph file
748 if (!@matches)
749 {
750 warn("WARNING: cannot find an entry for ".$gcov_file.
751 " in $graph_file_extension file, skipping ".
752 "file!\n");
753 unlink($gcov_file);
754 next;
755 }
756
757 # Read in contents of gcov file
758 @result = read_gcov_file($gcov_file);
759 @gcov_content = @{$result[0]};
760 @gcov_branches = @{$result[1]};
761 @gcov_functions = @{$result[2]};
762
763 # Skip empty files
764 if (!@gcov_content)
765 {
766 warn("WARNING: skipping empty file ".$gcov_file."\n");
767 unlink($gcov_file);
768 next;
769 }
770
771 if (scalar(@matches) == 1)
772 {
773 # Just one match
774 $source_filename = $matches[0];
775 }
776 else
777 {
778 # Try to solve the ambiguity
779 $source_filename = solve_ambiguous_match($gcov_file,
780 \@matches, \@gcov_content);
781 }
782
783 # Remove processed file from list
784 for ($index = scalar(@unprocessed) - 1; $index >= 0; $index--)
785 {
786 if ($unprocessed[$index] eq $source_filename)
787 {
788 splice(@unprocessed, $index, 1);
789 last;
790 }
791 }
792
793 # Write absolute path of source file
794 printf(INFO_HANDLE "SF:%s\n", $source_filename);
795
796 # Write function-related information
797 if (defined($bb_content{$source_filename}))
798 {
799 foreach (split(",",$bb_content{$source_filename}))
800 {
801 my ($fn, $line) = split("=", $_);
802
803 if ($fn eq "") {
804 next;
805 }
806
807 # Normalize function name
808 $fn =~ s/\W/_/g;
809
810 print(INFO_HANDLE "FN:$line,$fn\n");
811 }
812 }
813
814 #--
815 #-- FNDA: <call-count>, <function-name>
816 #-- FNF: overall count of functions
817 #-- FNH: overall count of functions with non-zero call count
818 #--
819 $funcs_found = 0;
820 $funcs_hit = 0;
821 while (@gcov_functions)
822 {
823 printf(INFO_HANDLE "FNDA:%s,%s\n",
824 $gcov_functions[0],
825 $gcov_functions[1]);
826 $funcs_found++;
827 $funcs_hit++ if $gcov_functions[0];
828 splice(@gcov_functions,0,2);
829 }
830 if ($funcs_found > 0) {
831 printf(INFO_HANDLE "FNF:%s\n", $funcs_found);
832 printf(INFO_HANDLE "FNH:%s\n", $funcs_hit);
833 }
834
835 # Reset line counters
836 $line_number = 0;
837 $lines_found = 0;
838 $lines_hit = 0;
839
840 # Write coverage information for each instrumented line
841 # Note: @gcov_content contains a list of (flag, count, source)
842 # tuple for each source code line
843 while (@gcov_content)
844 {
845 $line_number++;
846
847 # Check for instrumented line
848 if ($gcov_content[0])
849 {
850 $lines_found++;
851 printf(INFO_HANDLE "DA:".$line_number.",".
852 $gcov_content[1].($checksum ?
853 ",". md5_base64($gcov_content[2]) : "").
854 "\n");
855
856 # Increase $lines_hit in case of an execution
857 # count>0
858 if ($gcov_content[1] > 0) { $lines_hit++; }
859 }
860
861 # Remove already processed data from array
862 splice(@gcov_content,0,3);
863 }
864
865 #--
866 #-- BA: <code-line>, <branch-coverage>
867 #--
868 #-- print one BA line for every branch of a
869 #-- conditional. <branch-coverage> values
870 #-- are:
871 #-- 0 - not executed
872 #-- 1 - executed but not taken
873 #-- 2 - executed and taken
874 #--
875 while (@gcov_branches)
876 {
877 if ($gcov_branches[0])
878 {
879 printf(INFO_HANDLE "BA:%s,%s\n",
880 $gcov_branches[0],
881 $gcov_branches[1]);
882 }
883 splice(@gcov_branches,0,2);
884 }
885
886 # Write line statistics and section separator
887 printf(INFO_HANDLE "LF:%s\n", $lines_found);
888 printf(INFO_HANDLE "LH:%s\n", $lines_hit);
889 print(INFO_HANDLE "end_of_record\n");
890
891 # Remove .gcov file after processing
892 unlink($gcov_file);
893 }
894
895 # Check for files which show up in the graph file but were never
896 # processed
897 if (@unprocessed && @gcov_list)
898 {
899 foreach (@unprocessed)
900 {
901 warn("WARNING: no data found for $_\n");
902 }
903 }
904
905 if (!($output_filename && ($output_filename eq "-")))
906 {
907 close(INFO_HANDLE);
908 }
909
910 # Change back to initial directory
911 chdir($cwd);
912 }
913
914
915 #
916 # solve_relative_path(path, dir)
917 #
918 # Solve relative path components of DIR which, if not absolute, resides in PATH.
919 #
920
921 sub solve_relative_path($$)
922 {
923 my $path = $_[0];
924 my $dir = $_[1];
925 my $result;
926
927 $result = $dir;
928 # Prepend path if not absolute
929 if ($dir =~ /^[^\/]/)
930 {
931 $result = "$path/$result";
932 }
933
934 # Remove //
935 $result =~ s/\/\//\//g;
936
937 # Remove .
938 $result =~ s/\/\.\//\//g;
939
940 # Solve ..
941 while ($result =~ s/\/[^\/]+\/\.\.\//\//)
942 {
943 }
944
945 # Remove preceding ..
946 $result =~ s/^\/\.\.\//\//g;
947
948 return $result;
949 }
950
951
952 #
953 # match_filename(gcov_filename, list)
954 #
955 # Return a list of those entries of LIST which match the relative filename
956 # GCOV_FILENAME.
957 #
958
959 sub match_filename($@)
960 {
961 my $filename = shift;
962 my @list = @_;
963 my @result;
964
965 $filename =~ s/^(.*).gcov$/$1/;
966
967 if ($filename =~ /^\/(.*)$/)
968 {
969 $filename = "$1";
970 }
971
972 foreach (@list)
973 {
974 if (/\/\Q$filename\E(.*)$/ && $1 eq "")
975 {
976 @result = (@result, $_);
977 }
978 }
979 return @result;
980 }
981
982
983 #
984 # solve_ambiguous_match(rel_filename, matches_ref, gcov_content_ref)
985 #
986 # Try to solve ambiguous matches of mapping (gcov file) -> (source code) file
987 # by comparing source code provided in the GCOV file with that of the files
988 # in MATCHES. REL_FILENAME identifies the relative filename of the gcov
989 # file.
990 #
991 # Return the one real match or die if there is none.
992 #
993
994 sub solve_ambiguous_match($$$)
995 {
996 my $rel_name = $_[0];
997 my $matches = $_[1];
998 my $content = $_[2];
999 my $filename;
1000 my $index;
1001 my $no_match;
1002 local *SOURCE;
1003
1004 # Check the list of matches
1005 foreach $filename (@$matches)
1006 {
1007
1008 # Compare file contents
1009 open(SOURCE, $filename)
1010 or die("ERROR: cannot read $filename!\n");
1011
1012 $no_match = 0;
1013 for ($index = 2; <SOURCE>; $index += 3)
1014 {
1015 chomp;
1016
1017 if ($_ ne @$content[$index])
1018 {
1019 $no_match = 1;
1020 last;
1021 }
1022 }
1023
1024 close(SOURCE);
1025
1026 if (!$no_match)
1027 {
1028 info("Solved source file ambiguity for $rel_name\n");
1029 return $filename;
1030 }
1031 }
1032
1033 die("ERROR: could not match gcov data for $rel_name!\n");
1034 }
1035
1036
1037 #
1038 # split_filename(filename)
1039 #
1040 # Return (path, filename, extension) for a given FILENAME.
1041 #
1042
1043 sub split_filename($)
1044 {
1045 my @path_components = split('/', $_[0]);
1046 my @file_components = split('\.', pop(@path_components));
1047 my $extension = pop(@file_components);
1048
1049 return (join("/",@path_components), join(".",@file_components),
1050 $extension);
1051 }
1052
1053
1054 #
1055 # get_dir(filename);
1056 #
1057 # Return the directory component of a given FILENAME.
1058 #
1059
1060 sub get_dir($)
1061 {
1062 my @components = split("/", $_[0]);
1063 pop(@components);
1064
1065 return join("/", @components);
1066 }
1067
1068
1069 #
1070 # read_gcov_header(gcov_filename)
1071 #
1072 # Parse file GCOV_FILENAME and return a list containing the following
1073 # information:
1074 #
1075 # (source, object)
1076 #
1077 # where:
1078 #
1079 # source: complete relative path of the source code file (gcc >= 3.3 only)
1080 # object: name of associated graph file
1081 #
1082 # Die on error.
1083 #
1084
1085 sub read_gcov_header($)
1086 {
1087 my $source;
1088 my $object;
1089 local *INPUT;
1090
1091 if (!open(INPUT, $_[0]))
1092 {
1093 if ($ignore_errors[$ERROR_GCOV])
1094 {
1095 warn("WARNING: cannot read $_[0]!\n");
1096 return (undef,undef);
1097 }
1098 die("ERROR: cannot read $_[0]!\n");
1099 }
1100
1101 while (<INPUT>)
1102 {
1103 chomp($_);
1104
1105 if (/^\s+-:\s+0:Source:(.*)$/)
1106 {
1107 # Source: header entry
1108 $source = $1;
1109 }
1110 elsif (/^\s+-:\s+0:Object:(.*)$/)
1111 {
1112 # Object: header entry
1113 $object = $1;
1114 }
1115 else
1116 {
1117 last;
1118 }
1119 }
1120
1121 close(INPUT);
1122
1123 return ($source, $object);
1124 }
1125
1126
1127 #
1128 # read_gcov_file(gcov_filename)
1129 #
1130 # Parse file GCOV_FILENAME (.gcov file format) and return the list:
1131 # (reference to gcov_content, reference to gcov_branch, reference to gcov_func)
1132 #
1133 # gcov_content is a list of 3 elements
1134 # (flag, count, source) for each source code line:
1135 #
1136 # $result[($line_number-1)*3+0] = instrumentation flag for line $line_number
1137 # $result[($line_number-1)*3+1] = execution count for line $line_number
1138 # $result[($line_number-1)*3+2] = source code text for line $line_number
1139 #
1140 # gcov_branch is a list of 2 elements
1141 # (linenumber, branch result) for each branch
1142 #
1143 # gcov_func is a list of 2 elements
1144 # (number of calls, function name) for each function
1145 #
1146 # Die on error.
1147 #
1148
1149 sub read_gcov_file($)
1150 {
1151 my $filename = $_[0];
1152 my @result = ();
1153 my @branches = ();
1154 my @functions = ();
1155 my $number;
1156 local *INPUT;
1157
1158 open(INPUT, $filename)
1159 or die("ERROR: cannot read $filename!\n");
1160
1161 if ($gcov_version < $GCOV_VERSION_3_3_0)
1162 {
1163 # Expect gcov format as used in gcc < 3.3
1164 while (<INPUT>)
1165 {
1166 chomp($_);
1167
1168 if (/^\t\t(.*)$/)
1169 {
1170 # Uninstrumented line
1171 push(@result, 0);
1172 push(@result, 0);
1173 push(@result, $1);
1174 }
1175 elsif (/^branch/)
1176 {
1177 # Branch execution data
1178 push(@branches, scalar(@result) / 3);
1179 if (/^branch \d+ never executed$/)
1180 {
1181 push(@branches, 0);
1182 }
1183 elsif (/^branch \d+ taken = 0%/)
1184 {
1185 push(@branches, 1);
1186 }
1187 else
1188 {
1189 push(@branches, 2);
1190 }
1191 }
1192 elsif (/^call/ || /^function/)
1193 {
1194 # Function call return data
1195 }
1196 else
1197 {
1198 # Source code execution data
1199 $number = (split(" ",substr($_, 0, 16)))[0];
1200
1201 # Check for zero count which is indicated
1202 # by ######
1203 if ($number eq "######") { $number = 0; }
1204
1205 push(@result, 1);
1206 push(@result, $number);
1207 push(@result, substr($_, 16));
1208 }
1209 }
1210 }
1211 else
1212 {
1213 # Expect gcov format as used in gcc >= 3.3
1214 while (<INPUT>)
1215 {
1216 chomp($_);
1217
1218 if (/^branch\s+\d+\s+(\S+)\s+(\S+)/)
1219 {
1220 # Branch execution data
1221 push(@branches, scalar(@result) / 3);
1222 if ($1 eq "never")
1223 {
1224 push(@branches, 0);
1225 }
1226 elsif ($2 eq "0%")
1227 {
1228 push(@branches, 1);
1229 }
1230 else
1231 {
1232 push(@branches, 2);
1233 }
1234 }
1235 elsif (/^function\s+(\S+)\s+called\s+(\d+)/)
1236 {
1237 push(@functions, $2, $1);
1238 }
1239 elsif (/^call/)
1240 {
1241 # Function call return data
1242 }
1243 elsif (/^\s*([^:]+):\s*([^:]+):(.*)$/)
1244 {
1245 # <exec count>:<line number>:<source code>
1246 if ($2 eq "0")
1247 {
1248 # Extra data
1249 }
1250 elsif ($1 eq "-")
1251 {
1252 # Uninstrumented line
1253 push(@result, 0);
1254 push(@result, 0);
1255 push(@result, $3);
1256 }
1257 else
1258 {
1259 # Source code execution data
1260 $number = $1;
1261
1262 # Check for zero count
1263 if ($number eq "#####") { $number = 0; }
1264
1265 push(@result, 1);
1266 push(@result, $number);
1267 push(@result, $3);
1268 }
1269 }
1270 }
1271 }
1272
1273 close(INPUT);
1274 return(\@result, \@branches, \@functions);
1275 }
1276
1277
1278 #
1279 # read_bb_file(bb_filename, base_dir)
1280 #
1281 # Read .bb file BB_FILENAME and return a hash containing the following
1282 # mapping:
1283 #
1284 # filename -> comma-separated list of pairs (function name=starting
1285 # line number) to indicate the starting line of a function or
1286 # =name to indicate an instrumented line
1287 #
1288 # for each entry in the .bb file. Filenames are absolute, i.e. relative
1289 # filenames are prefixed with BASE_DIR.
1290 #
1291 # Die on error.
1292 #
1293
1294 sub read_bb_file($$)
1295 {
1296 my $bb_filename = $_[0];
1297 my $base_dir = $_[1];
1298 my %result;
1299 my $filename;
1300 my $function_name;
1301 my $minus_one = sprintf("%d", 0x80000001);
1302 my $minus_two = sprintf("%d", 0x80000002);
1303 my $value;
1304 my $packed_word;
1305 local *INPUT;
1306
1307 open(INPUT, $bb_filename)
1308 or die("ERROR: cannot read $bb_filename!\n");
1309
1310 binmode(INPUT);
1311
1312 # Read data in words of 4 bytes
1313 while (read(INPUT, $packed_word, 4) == 4)
1314 {
1315 # Decode integer in intel byteorder
1316 $value = unpack_int32($packed_word, 0);
1317
1318 # Note: the .bb file format is documented in GCC info pages
1319 if ($value == $minus_one)
1320 {
1321 # Filename follows
1322 $filename = read_string(*INPUT, $minus_one)
1323 or die("ERROR: incomplete filename in ".
1324 "$bb_filename!\n");
1325
1326 # Make path absolute
1327 $filename = solve_relative_path($base_dir, $filename);
1328
1329 # Insert into hash if not yet present.
1330 # This is necessary because functions declared as
1331 # "inline" are not listed as actual functions in
1332 # .bb files
1333 if (!$result{$filename})
1334 {
1335 $result{$filename}="";
1336 }
1337 }
1338 elsif ($value == $minus_two)
1339 {
1340 # Function name follows
1341 $function_name = read_string(*INPUT, $minus_two)
1342 or die("ERROR: incomplete function ".
1343 "name in $bb_filename!\n");
1344 $function_name =~ s/\W/_/g;
1345 }
1346 elsif ($value > 0)
1347 {
1348 if (defined($filename))
1349 {
1350 $result{$filename} .=
1351 ($result{$filename} ? "," : "").
1352 "=$value";
1353 }
1354 else
1355 {
1356 warn("WARNING: unassigned line".
1357 " number in .bb file ".
1358 "$bb_filename\n");
1359 }
1360 if ($function_name)
1361 {
1362 # Got a full entry filename, funcname, lineno
1363 # Add to resulting hash
1364
1365 $result{$filename}.=
1366 ($result{$filename} ? "," : "").
1367 join("=",($function_name,$value));
1368 undef($function_name);
1369 }
1370 }
1371 }
1372 close(INPUT);
1373
1374 if (!scalar(keys(%result)))
1375 {
1376 die("ERROR: no data found in $bb_filename!\n");
1377 }
1378 return %result;
1379 }
1380
1381
1382 #
1383 # read_string(handle, delimiter);
1384 #
1385 # Read and return a string in 4-byte chunks from HANDLE until DELIMITER
1386 # is found.
1387 #
1388 # Return empty string on error.
1389 #
1390
1391 sub read_string(*$)
1392 {
1393 my $HANDLE = $_[0];
1394 my $delimiter = $_[1];
1395 my $string = "";
1396 my $packed_word;
1397 my $value;
1398
1399 while (read($HANDLE,$packed_word,4) == 4)
1400 {
1401 $value = unpack_int32($packed_word, 0);
1402
1403 if ($value == $delimiter)
1404 {
1405 # Remove trailing nil bytes
1406 $/="\0";
1407 while (chomp($string)) {};
1408 $/="\n";
1409 return($string);
1410 }
1411
1412 $string = $string.$packed_word;
1413 }
1414 return("");
1415 }
1416
1417
1418 #
1419 # read_gcno_file(bb_filename, base_dir)
1420 #
1421 # Read .gcno file BB_FILENAME and return a hash containing the following
1422 # mapping:
1423 #
1424 # filename -> comma-separated list of pairs (function name=starting
1425 # line number) to indicate the starting line of a function or
1426 # =name to indicate an instrumented line
1427 #
1428 # for each entry in the .gcno file. Filenames are absolute, i.e. relative
1429 # filenames are prefixed with BASE_DIR.
1430 #
1431 # Die on error.
1432 #
1433
1434 sub read_gcno_file($$)
1435 {
1436 my $gcno_filename = $_[0];
1437 my $base_dir = $_[1];
1438 my %result;
1439 my $filename;
1440 my $function_name;
1441 my $lineno;
1442 my $length;
1443 my $value;
1444 my $endianness;
1445 my $blocks;
1446 my $packed_word;
1447 my $string;
1448 local *INPUT;
1449
1450 open(INPUT, $gcno_filename)
1451 or die("ERROR: cannot read $gcno_filename!\n");
1452
1453 binmode(INPUT);
1454
1455 read(INPUT, $packed_word, 4) == 4
1456 or die("ERROR: Invalid gcno file format\n");
1457
1458 $value = unpack_int32($packed_word, 0);
1459 $endianness = !($value == $GCNO_FILE_MAGIC);
1460
1461 unpack_int32($packed_word, $endianness) == $GCNO_FILE_MAGIC
1462 or die("ERROR: gcno file magic does not match\n");
1463
1464 seek(INPUT, 8, 1);
1465
1466 # Read data in words of 4 bytes
1467 while (read(INPUT, $packed_word, 4) == 4)
1468 {
1469 # Decode integer in intel byteorder
1470 $value = unpack_int32($packed_word, $endianness);
1471
1472 if ($value == $GCNO_FUNCTION_TAG)
1473 {
1474 # skip length, ident and checksum
1475 seek(INPUT, 12, 1);
1476 (undef, $function_name) =
1477 read_gcno_string(*INPUT, $endianness);
1478 $function_name =~ s/\W/_/g;
1479 (undef, $filename) =
1480 read_gcno_string(*INPUT, $endianness);
1481 $filename = solve_relative_path($base_dir, $filename);
1482
1483 read(INPUT, $packed_word, 4);
1484 $lineno = unpack_int32($packed_word, $endianness);
1485
1486 $result{$filename}.=
1487 ($result{$filename} ? "," : "").
1488 join("=",($function_name,$lineno));
1489 }
1490 elsif ($value == $GCNO_LINES_TAG)
1491 {
1492 # Check for names of files containing inlined code
1493 # included in this file
1494 read(INPUT, $packed_word, 4);
1495 $length = unpack_int32($packed_word, $endianness);
1496 if ($length > 0)
1497 {
1498 # Block number
1499 read(INPUT, $packed_word, 4);
1500 $length--;
1501 }
1502 while ($length > 0)
1503 {
1504 read(INPUT, $packed_word, 4);
1505 $lineno = unpack_int32($packed_word,
1506 $endianness);
1507 $length--;
1508 if ($lineno != 0)
1509 {
1510 if (defined($filename))
1511 {
1512 $result{$filename} .=
1513 ($result{$filename} ? ", " : "").
1514 "=$lineno";
1515 }
1516 else
1517 {
1518 warn("WARNING: unassigned line".
1519 " number in .gcno file ".
1520 "$gcno_filename\n");
1521 }
1522 next;
1523 }
1524 last if ($length == 0);
1525 ($blocks, $string) =
1526 read_gcno_string(*INPUT, $endianness);
1527 if (defined($string))
1528 {
1529 $filename = $string;
1530 }
1531 if ($blocks > 1)
1532 {
1533 $filename = solve_relative_path(
1534 $base_dir, $filename);
1535 if (!defined($result{$filename}))
1536 {
1537 $result{$filename} = "";
1538 }
1539 }
1540 $length -= $blocks;
1541 }
1542 }
1543 else
1544 {
1545 read(INPUT, $packed_word, 4);
1546 $length = unpack_int32($packed_word, $endianness);
1547 seek(INPUT, 4 * $length, 1);
1548 }
1549 }
1550 close(INPUT);
1551
1552 if (!scalar(keys(%result)))
1553 {
1554 die("ERROR: no data found in $gcno_filename!\n");
1555 }
1556 return %result;
1557 }
1558
1559
1560 #
1561 # read_gcno_string(handle, endianness);
1562 #
1563 # Read a string in 4-byte chunks from HANDLE.
1564 #
1565 # Return (number of 4-byte chunks read, string).
1566 #
1567
1568 sub read_gcno_string(*$)
1569 {
1570 my $handle = $_[0];
1571 my $endianness = $_[1];
1572 my $number_of_blocks = 0;
1573 my $string = "";
1574 my $packed_word;
1575
1576 read($handle, $packed_word, 4) == 4
1577 or die("ERROR: reading string\n");
1578
1579 $number_of_blocks = unpack_int32($packed_word, $endianness);
1580
1581 if ($number_of_blocks == 0)
1582 {
1583 return (1, undef);
1584 }
1585
1586 if (read($handle, $packed_word, 4 * $number_of_blocks) !=
1587 4 * $number_of_blocks)
1588 {
1589 my $msg = "invalid string size ".(4 * $number_of_blocks)." in ".
1590 "gcno file at position ".tell($handle)."\n";
1591 if ($ignore[$ERROR_SOURCE])
1592 {
1593 warn("WARNING: $msg");
1594 return (1, undef);
1595 }
1596 else
1597 {
1598 die("ERROR: $msg");
1599 }
1600 }
1601
1602 $string = $string . $packed_word;
1603
1604 # Remove trailing nil bytes
1605 $/="\0";
1606 while (chomp($string)) {};
1607 $/="\n";
1608
1609 return(1 + $number_of_blocks, $string);
1610 }
1611
1612
1613 #
1614 # read_hammer_bbg_file(bb_filename, base_dir)
1615 #
1616 # Read .bbg file BB_FILENAME and return a hash containing the following
1617 # mapping:
1618 #
1619 # filename -> comma-separated list of pairs (function name=starting
1620 # line number) to indicate the starting line of a function or
1621 # =name to indicate an instrumented line
1622 #
1623 # for each entry in the .bbg file. Filenames are absolute, i.e. relative
1624 # filenames are prefixed with BASE_DIR.
1625 #
1626 # Die on error.
1627 #
1628
1629 sub read_hammer_bbg_file($$)
1630 {
1631 my $bbg_filename = $_[0];
1632 my $base_dir = $_[1];
1633 my %result;
1634 my $filename;
1635 my $function_name;
1636 my $first_line;
1637 my $lineno;
1638 my $length;
1639 my $value;
1640 my $endianness;
1641 my $blocks;
1642 my $packed_word;
1643 local *INPUT;
1644
1645 open(INPUT, $bbg_filename)
1646 or die("ERROR: cannot read $bbg_filename!\n");
1647
1648 binmode(INPUT);
1649
1650 # Read magic
1651 read(INPUT, $packed_word, 4) == 4
1652 or die("ERROR: invalid bbg file format\n");
1653
1654 $endianness = 1;
1655
1656 unpack_int32($packed_word, $endianness) == $BBG_FILE_MAGIC
1657 or die("ERROR: bbg file magic does not match\n");
1658
1659 # Skip version
1660 seek(INPUT, 4, 1);
1661
1662 # Read data in words of 4 bytes
1663 while (read(INPUT, $packed_word, 4) == 4)
1664 {
1665 # Get record tag
1666 $value = unpack_int32($packed_word, $endianness);
1667
1668 # Get record length
1669 read(INPUT, $packed_word, 4);
1670 $length = unpack_int32($packed_word, $endianness);
1671
1672 if ($value == $GCNO_FUNCTION_TAG)
1673 {
1674 # Get function name
1675 ($value, $function_name) =
1676 read_hammer_bbg_string(*INPUT, $endianness);
1677 $function_name =~ s/\W/_/g;
1678 $filename = undef;
1679 $first_line = undef;
1680
1681 seek(INPUT, $length - $value * 4, 1);
1682 }
1683 elsif ($value == $GCNO_LINES_TAG)
1684 {
1685 # Get linenumber and filename
1686 # Skip block number
1687 seek(INPUT, 4, 1);
1688 $length -= 4;
1689
1690 while ($length > 0)
1691 {
1692 read(INPUT, $packed_word, 4);
1693 $lineno = unpack_int32($packed_word,
1694 $endianness);
1695 $length -= 4;
1696 if ($lineno != 0)
1697 {
1698 if (!defined($first_line))
1699 {
1700 $first_line = $lineno;
1701 }
1702 if (defined($filename))
1703 {
1704 $result{$filename} .=
1705 ($result{$filename} ? ", " : "").
1706 "=$lineno";
1707 }
1708 else
1709 {
1710 warn("WARNING: unassigned line".
1711 " number in .bbg file ".
1712 "$bbg_filename\n");
1713 }
1714 next;
1715 }
1716 ($blocks, $value) =
1717 read_hammer_bbg_string(
1718 *INPUT, $endianness);
1719 # Add all filenames to result list
1720 if (defined($value))
1721 {
1722 $value = solve_relative_path(
1723 $base_dir, $value);
1724 if (!defined($result{$value}))
1725 {
1726 $result{$value} = undef;
1727 }
1728 if (!defined($filename))
1729 {
1730 $filename = $value;
1731 }
1732 }
1733 $length -= $blocks * 4;
1734
1735 # Got a complete data set?
1736 if (defined($filename) &&
1737 defined($first_line) &&
1738 defined($function_name))
1739 {
1740 # Add it to our result hash
1741 if (defined($result{$filename}))
1742 {
1743 $result{$filename} .=
1744 ",$function_name=$first_line";
1745 }
1746 else
1747 {
1748 $result{$filename} =
1749 "$function_name=$first_line";
1750 }
1751 $function_name = undef;
1752 $filename = undef;
1753 $first_line = undef;
1754 }
1755 }
1756 }
1757 else
1758 {
1759 # Skip other records
1760 seek(INPUT, $length, 1);
1761 }
1762 }
1763 close(INPUT);
1764
1765 if (!scalar(keys(%result)))
1766 {
1767 die("ERROR: no data found in $bbg_filename!\n");
1768 }
1769 return %result;
1770 }
1771
1772
1773 #
1774 # read_hammer_bbg_string(handle, endianness);
1775 #
1776 # Read a string in 4-byte chunks from HANDLE.
1777 #
1778 # Return (number of 4-byte chunks read, string).
1779 #
1780
1781 sub read_hammer_bbg_string(*$)
1782 {
1783 my $handle = $_[0];
1784 my $endianness = $_[1];
1785 my $length = 0;
1786 my $string = "";
1787 my $packed_word;
1788 my $pad;
1789
1790 read($handle, $packed_word, 4) == 4
1791 or die("ERROR: reading string\n");
1792
1793 $length = unpack_int32($packed_word, $endianness);
1794 $pad = 4 - $length % 4;
1795
1796 if ($length == 0)
1797 {
1798 return (1, undef);
1799 }
1800
1801 read($handle, $string, $length) ==
1802 $length or die("ERROR: reading string\n");
1803 seek($handle, $pad, 1);
1804
1805 return(1 + ($length + $pad) / 4, $string);
1806 }
1807
1808 #
1809 # unpack_int32(word, endianness)
1810 #
1811 # Interpret 4-byte binary string WORD as signed 32 bit integer in
1812 # endian encoding defined by ENDIANNESS (0=little, 1=big) and return its
1813 # value.
1814 #
1815
1816 sub unpack_int32($$)
1817 {
1818 return sprintf("%d", unpack($_[1] ? "N" : "V",$_[0]));
1819 }
1820
1821
1822 #
1823 # Get the GCOV tool version. Return an integer number which represents the
1824 # GCOV version. Version numbers can be compared using standard integer
1825 # operations.
1826 #
1827
1828 sub get_gcov_version()
1829 {
1830 local *HANDLE;
1831 my $version_string;
1832 my $result;
1833
1834 open(GCOV_PIPE, "$gcov_tool -v |")
1835 or die("ERROR: cannot retrieve gcov version!\n");
1836 $version_string = <GCOV_PIPE>;
1837 close(GCOV_PIPE);
1838
1839 $result = 0;
1840 if ($version_string =~ /(\d+)\.(\d+)(\.(\d+))?/)
1841 {
1842 if (defined($4))
1843 {
1844 info("Found gcov version: $1.$2.$4\n");
1845 $result = $1 << 16 | $2 << 8 | $4;
1846 }
1847 else
1848 {
1849 info("Found gcov version: $1.$2\n");
1850 $result = $1 << 16 | $2 << 8;
1851 }
1852 }
1853 if ($version_string =~ /suse/i && $result == 0x30303 ||
1854 $version_string =~ /mandrake/i && $result == 0x30302)
1855 {
1856 info("Using compatibility mode for GCC 3.3 (hammer)\n");
1857 $compatibility = $COMPAT_HAMMER;
1858 }
1859 return $result;
1860 }
1861
1862
1863 #
1864 # info(printf_parameter)
1865 #
1866 # Use printf to write PRINTF_PARAMETER to stdout only when the $quiet flag
1867 # is not set.
1868 #
1869
1870 sub info(@)
1871 {
1872 if (!$quiet)
1873 {
1874 # Print info string
1875 if (defined($output_filename) && ($output_filename eq "-"))
1876 {
1877 # Don't interfere with the .info output to STDOUT
1878 printf(STDERR @_);
1879 }
1880 else
1881 {
1882 printf(@_);
1883 }
1884 }
1885 }
1886
1887
1888 #
1889 # int_handler()
1890 #
1891 # Called when the script was interrupted by an INT signal (e.g. CTRl-C)
1892 #
1893
1894 sub int_handler()
1895 {
1896 if ($cwd) { chdir($cwd); }
1897 info("Aborted.\n");
1898 exit(1);
1899 }
1900
1901
1902 #
1903 # system_no_output(mode, parameters)
1904 #
1905 # Call an external program using PARAMETERS while suppressing depending on
1906 # the value of MODE:
1907 #
1908 # MODE & 1: suppress STDOUT
1909 # MODE & 2: suppress STDERR
1910 #
1911 # Return 0 on success, non-zero otherwise.
1912 #
1913
1914 sub system_no_output($@)
1915 {
1916 my $mode = shift;
1917 my $result;
1918 local *OLD_STDERR;
1919 local *OLD_STDOUT;
1920
1921 # Save old stdout and stderr handles
1922 ($mode & 1) && open(OLD_STDOUT, ">>&STDOUT");
1923 ($mode & 2) && open(OLD_STDERR, ">>&STDERR");
1924
1925 # Redirect to /dev/null
1926 ($mode & 1) && open(STDOUT, ">/dev/null");
1927 ($mode & 2) && open(STDERR, ">/dev/null");
1928
1929 system(@_);
1930 $result = $?;
1931
1932 # Close redirected handles
1933 ($mode & 1) && close(STDOUT);
1934 ($mode & 2) && close(STDERR);
1935
1936 # Restore old handles
1937 ($mode & 1) && open(STDOUT, ">>&OLD_STDOUT");
1938 ($mode & 2) && open(STDERR, ">>&OLD_STDERR");
1939
1940 return $result;
1941 }
1942
1943
1944 #
1945 # read_config(filename)
1946 #
1947 # Read configuration file FILENAME and return a reference to a hash containing
1948 # all valid key=value pairs found.
1949 #
1950
1951 sub read_config($)
1952 {
1953 my $filename = $_[0];
1954 my %result;
1955 my $key;
1956 my $value;
1957 local *HANDLE;
1958
1959 if (!open(HANDLE, "<$filename"))
1960 {
1961 warn("WARNING: cannot read configuration file $filename\n");
1962 return undef;
1963 }
1964 while (<HANDLE>)
1965 {
1966 chomp;
1967 # Skip comments
1968 s/#.*//;
1969 # Remove leading blanks
1970 s/^\s+//;
1971 # Remove trailing blanks
1972 s/\s+$//;
1973 next unless length;
1974 ($key, $value) = split(/\s*=\s*/, $_, 2);
1975 if (defined($key) && defined($value))
1976 {
1977 $result{$key} = $value;
1978 }
1979 else
1980 {
1981 warn("WARNING: malformed statement in line $. ".
1982 "of configuration file $filename\n");
1983 }
1984 }
1985 close(HANDLE);
1986 return \%result;
1987 }
1988
1989
1990 #
1991 # apply_config(REF)
1992 #
1993 # REF is a reference to a hash containing the following mapping:
1994 #
1995 # key_string => var_ref
1996 #
1997 # where KEY_STRING is a keyword and VAR_REF is a reference to an associated
1998 # variable. If the global configuration hash CONFIG contains a value for
1999 # keyword KEY_STRING, VAR_REF will be assigned the value for that keyword.
2000 #
2001
2002 sub apply_config($)
2003 {
2004 my $ref = $_[0];
2005
2006 foreach (keys(%{$ref}))
2007 {
2008 if (defined($config->{$_}))
2009 {
2010 ${$ref->{$_}} = $config->{$_};
2011 }
2012 }
2013 }
2014
2015
2016 sub gen_initial_info($)
2017 {
2018 my $directory = $_[0];
2019 my @file_list;
2020
2021 if (-d $directory)
2022 {
2023 info("Scanning $directory for $graph_file_extension ".
2024 "files ...\n");
2025
2026 @file_list = `find "$directory" $maxdepth $follow -name \\*$grap h_file_extension -type f 2>/dev/null`;
2027 chomp(@file_list);
2028 @file_list or die("ERROR: no $graph_file_extension files ".
2029 "found in $directory!\n");
2030 info("Found %d graph files in %s\n", $#file_list+1, $directory);
2031 }
2032 else
2033 {
2034 @file_list = ($directory);
2035 }
2036
2037 # Process all files in list
2038 foreach (@file_list) { process_graphfile($_); }
2039 }
2040
2041 sub process_graphfile($)
2042 {
2043 my $graph_filename = $_[0];
2044 my $graph_dir;
2045 my $graph_basename;
2046 my $source_dir;
2047 my $base_dir;
2048 my %graph_data;
2049 my $filename;
2050 local *INFO_HANDLE;
2051
2052 info("Processing $_[0]\n");
2053
2054 # Get path to data file in absolute and normalized form (begins with /,
2055 # contains no more ../ or ./)
2056 $graph_filename = solve_relative_path($cwd, $graph_filename);
2057
2058 # Get directory and basename of data file
2059 ($graph_dir, $graph_basename) = split_filename($graph_filename);
2060
2061 # avoid files from .libs dirs
2062 if ($compat_libtool && $graph_dir =~ m/(.*)\/\.libs$/) {
2063 $source_dir = $1;
2064 } else {
2065 $source_dir = $graph_dir;
2066 }
2067
2068 # Construct base_dir for current file
2069 if ($base_directory)
2070 {
2071 $base_dir = $base_directory;
2072 }
2073 else
2074 {
2075 $base_dir = $source_dir;
2076 }
2077
2078 if ($gcov_version < $GCOV_VERSION_3_4_0)
2079 {
2080 if (defined($compatibility) && $compatibility eq $COMPAT_HAMMER)
2081 {
2082 %graph_data = read_hammer_bbg_file($graph_filename,
2083 $base_dir);
2084 }
2085 else
2086 {
2087 %graph_data = read_bb_file($graph_filename, $base_dir);
2088 }
2089 }
2090 else
2091 {
2092 %graph_data = read_gcno_file($graph_filename, $base_dir);
2093 }
2094
2095 # Check whether we're writing to a single file
2096 if ($output_filename)
2097 {
2098 if ($output_filename eq "-")
2099 {
2100 *INFO_HANDLE = *STDOUT;
2101 }
2102 else
2103 {
2104 # Append to output file
2105 open(INFO_HANDLE, ">>$output_filename")
2106 or die("ERROR: cannot write to ".
2107 "$output_filename!\n");
2108 }
2109 }
2110 else
2111 {
2112 # Open .info file for output
2113 open(INFO_HANDLE, ">$graph_filename.info")
2114 or die("ERROR: cannot create $graph_filename.info!\n");
2115 }
2116
2117 # Write test name
2118 printf(INFO_HANDLE "TN:%s\n", $test_name);
2119 foreach $filename (keys(%graph_data))
2120 {
2121 my %lines;
2122 my $count = 0;
2123 my @functions;
2124
2125 print(INFO_HANDLE "SF:$filename\n");
2126
2127 # Write function related data
2128 foreach (split(",",$graph_data{$filename}))
2129 {
2130 my ($fn, $line) = split("=", $_);
2131
2132 if ($fn eq "")
2133 {
2134 $lines{$line} = "";
2135 next;
2136 }
2137
2138 # Normalize function name
2139 $fn =~ s/\W/_/g;
2140
2141 print(INFO_HANDLE "FN:$line,$fn\n");
2142 push(@functions, $fn);
2143 }
2144 foreach (@functions) {
2145 print(INFO_HANDLE "FNDA:$_,0\n");
2146 }
2147 print(INFO_HANDLE "FNF:".scalar(@functions)."\n");
2148 print(INFO_HANDLE "FNH:0\n");
2149
2150 # Write line related data
2151 foreach (sort {$a <=> $b } keys(%lines))
2152 {
2153 print(INFO_HANDLE "DA:$_,0\n");
2154 $count++;
2155 }
2156 print(INFO_HANDLE "LH:0\n");
2157 print(INFO_HANDLE "LF:$count\n");
2158 print(INFO_HANDLE "end_of_record\n");
2159 }
2160 if (!($output_filename && ($output_filename eq "-")))
2161 {
2162 close(INFO_HANDLE);
2163 }
2164 }
2165
2166 sub warn_handler($)
2167 {
2168 my ($msg) = @_;
2169
2170 warn("$tool_name: $msg");
2171 }
2172
2173 sub die_handler($)
2174 {
2175 my ($msg) = @_;
2176
2177 die("$tool_name: $msg");
2178 }
OLDNEW
« no previous file with comments | « third_party/lcov/bin/genhtml ('k') | third_party/lcov/bin/genpng » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698