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

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

Issue 23189008: Upgrades lcov to 1.10, removes lcov-1.9 (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Re-adds UNKNOWN suppression Created 7 years, 4 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
« 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')
OLDNEW
1 #!/usr/bin/perl -w 1 #!/usr/bin/perl -w
2 # 2 #
3 # Copyright (c) International Business Machines Corp., 2002,2007 3 # Copyright (c) International Business Machines Corp., 2002,2012
4 # 4 #
5 # This program is free software; you can redistribute it and/or modify 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 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 7 # the Free Software Foundation; either version 2 of the License, or (at
8 # your option) any later version. 8 # your option) any later version.
9 # 9 #
10 # This program is distributed in the hope that it will be useful, but 10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of 11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 # General Public License for more details. 13 # General Public License for more details.
(...skipping 30 matching lines...) Expand all
44 # 2004-02-16 / Andreas Krebbel: Added support for .gcno/.gcda files and 44 # 2004-02-16 / Andreas Krebbel: Added support for .gcno/.gcda files and
45 # gcov versioning 45 # gcov versioning
46 # 2004-08-09 / Peter Oberparleiter: added configuration file support 46 # 2004-08-09 / Peter Oberparleiter: added configuration file support
47 # 2008-07-14 / Tom Zoerner: added --function-coverage command line option 47 # 2008-07-14 / Tom Zoerner: added --function-coverage command line option
48 # 2008-08-13 / Peter Oberparleiter: modified function coverage 48 # 2008-08-13 / Peter Oberparleiter: modified function coverage
49 # implementation (now enabled per default) 49 # implementation (now enabled per default)
50 # 50 #
51 51
52 use strict; 52 use strict;
53 use File::Basename; 53 use File::Basename;
54 use File::Spec::Functions qw /abs2rel catdir file_name_is_absolute splitdir
55 splitpath catpath/;
54 use Getopt::Long; 56 use Getopt::Long;
55 use Digest::MD5 qw(md5_base64); 57 use Digest::MD5 qw(md5_base64);
56 58 if( $^O eq "msys" )
59 {
60 » require File::Spec::Win32;
61 }
57 62
58 # Constants 63 # Constants
59 our $lcov_version» = "LCOV version 1.7"; 64 our $lcov_version» = 'LCOV version 1.10';
60 our $lcov_url = "http://ltp.sourceforge.net/coverage/lcov.php"; 65 our $lcov_url = "http://ltp.sourceforge.net/coverage/lcov.php";
61 our $gcov_tool = "gcov"; 66 our $gcov_tool = "gcov";
62 our $tool_name = basename($0); 67 our $tool_name = basename($0);
63 68
69 our $GCOV_VERSION_4_7_0 = 0x40700;
64 our $GCOV_VERSION_3_4_0 = 0x30400; 70 our $GCOV_VERSION_3_4_0 = 0x30400;
65 our $GCOV_VERSION_3_3_0 = 0x30300; 71 our $GCOV_VERSION_3_3_0 = 0x30300;
66 our $GCNO_FUNCTION_TAG = 0x01000000; 72 our $GCNO_FUNCTION_TAG = 0x01000000;
67 our $GCNO_LINES_TAG = 0x01450000; 73 our $GCNO_LINES_TAG = 0x01450000;
68 our $GCNO_FILE_MAGIC = 0x67636e6f; 74 our $GCNO_FILE_MAGIC = 0x67636e6f;
69 our $BBG_FILE_MAGIC = 0x67626267; 75 our $BBG_FILE_MAGIC = 0x67626267;
70 76
71 our $COMPAT_HAMMER» = "hammer"; 77 # Error classes which users may specify to ignore during processing
72
73 our $ERROR_GCOV = 0; 78 our $ERROR_GCOV = 0;
74 our $ERROR_SOURCE = 1; 79 our $ERROR_SOURCE = 1;
80 our $ERROR_GRAPH = 2;
81 our %ERROR_ID = (
82 "gcov" => $ERROR_GCOV,
83 "source" => $ERROR_SOURCE,
84 "graph" => $ERROR_GRAPH,
85 );
86
87 our $EXCL_START = "LCOV_EXCL_START";
88 our $EXCL_STOP = "LCOV_EXCL_STOP";
89 our $EXCL_LINE = "LCOV_EXCL_LINE";
90
91 # Compatibility mode values
92 our $COMPAT_VALUE_OFF = 0;
93 our $COMPAT_VALUE_ON = 1;
94 our $COMPAT_VALUE_AUTO = 2;
95
96 # Compatibility mode value names
97 our %COMPAT_NAME_TO_VALUE = (
98 "off" => $COMPAT_VALUE_OFF,
99 "on" => $COMPAT_VALUE_ON,
100 "auto" => $COMPAT_VALUE_AUTO,
101 );
102
103 # Compatiblity modes
104 our $COMPAT_MODE_LIBTOOL = 1 << 0;
105 our $COMPAT_MODE_HAMMER = 1 << 1;
106 our $COMPAT_MODE_SPLIT_CRC = 1 << 2;
107
108 # Compatibility mode names
109 our %COMPAT_NAME_TO_MODE = (
110 "libtool" => $COMPAT_MODE_LIBTOOL,
111 "hammer" => $COMPAT_MODE_HAMMER,
112 "split_crc" => $COMPAT_MODE_SPLIT_CRC,
113 "android_4_4_0" => $COMPAT_MODE_SPLIT_CRC,
114 );
115
116 # Map modes to names
117 our %COMPAT_MODE_TO_NAME = (
118 $COMPAT_MODE_LIBTOOL => "libtool",
119 $COMPAT_MODE_HAMMER => "hammer",
120 $COMPAT_MODE_SPLIT_CRC => "split_crc",
121 );
122
123 # Compatibility mode default values
124 our %COMPAT_MODE_DEFAULTS = (
125 $COMPAT_MODE_LIBTOOL => $COMPAT_VALUE_ON,
126 $COMPAT_MODE_HAMMER => $COMPAT_VALUE_AUTO,
127 $COMPAT_MODE_SPLIT_CRC => $COMPAT_VALUE_AUTO,
128 );
129
130 # Compatibility mode auto-detection routines
131 sub compat_hammer_autodetect();
132 our %COMPAT_MODE_AUTO = (
133 $COMPAT_MODE_HAMMER => \&compat_hammer_autodetect,
134 $COMPAT_MODE_SPLIT_CRC => 1, # will be done later
135 );
136
137 our $BR_LINE = 0;
138 our $BR_BLOCK = 1;
139 our $BR_BRANCH = 2;
140 our $BR_TAKEN = 3;
141 our $BR_VEC_ENTRIES = 4;
142 our $BR_VEC_WIDTH = 32;
143
144 our $UNNAMED_BLOCK = 9999;
75 145
76 # Prototypes 146 # Prototypes
77 sub print_usage(*); 147 sub print_usage(*);
78 sub gen_info($); 148 sub gen_info($);
79 sub process_dafile($); 149 sub process_dafile($$);
80 sub match_filename($@); 150 sub match_filename($@);
81 sub solve_ambiguous_match($$$); 151 sub solve_ambiguous_match($$$);
82 sub split_filename($); 152 sub split_filename($);
83 sub solve_relative_path($$); 153 sub solve_relative_path($$);
84 sub get_dir($);
85 sub read_gcov_header($); 154 sub read_gcov_header($);
86 sub read_gcov_file($); 155 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(@); 156 sub info(@);
95 sub get_gcov_version(); 157 sub get_gcov_version();
96 sub system_no_output($@); 158 sub system_no_output($@);
97 sub read_config($); 159 sub read_config($);
98 sub apply_config($); 160 sub apply_config($);
99 sub gen_initial_info($); 161 sub get_exclusion_data($);
100 sub process_graphfile($); 162 sub apply_exclusion_data($$);
163 sub process_graphfile($$);
164 sub filter_fn_name($);
101 sub warn_handler($); 165 sub warn_handler($);
102 sub die_handler($); 166 sub die_handler($);
167 sub graph_error($$);
168 sub graph_expect($);
169 sub graph_read(*$;$$);
170 sub graph_skip(*$;$);
171 sub sort_uniq(@);
172 sub sort_uniq_lex(@);
173 sub graph_cleanup($);
174 sub graph_find_base($);
175 sub graph_from_bb($$$);
176 sub graph_add_order($$$);
177 sub read_bb_word(*;$);
178 sub read_bb_value(*;$);
179 sub read_bb_string(*$);
180 sub read_bb($);
181 sub read_bbg_word(*;$);
182 sub read_bbg_value(*;$);
183 sub read_bbg_string(*);
184 sub read_bbg_lines_record(*$$$$$);
185 sub read_bbg($);
186 sub read_gcno_word(*;$$);
187 sub read_gcno_value(*$;$$);
188 sub read_gcno_string(*$);
189 sub read_gcno_lines_record(*$$$$$$);
190 sub determine_gcno_split_crc($$$);
191 sub read_gcno_function_record(*$$$$);
192 sub read_gcno($);
193 sub get_gcov_capabilities();
194 sub get_overall_line($$$$);
195 sub print_overall_rate($$$$$$$$$);
196 sub br_gvec_len($);
197 sub br_gvec_get($$);
198 sub debug($);
199 sub int_handler();
200 sub parse_ignore_errors(@);
201 sub is_external($);
202 sub compat_name($);
203 sub parse_compat_modes($);
204 sub is_compat($);
205 sub is_compat_auto($);
206
103 207
104 # Global variables 208 # Global variables
105 our $gcov_version; 209 our $gcov_version;
210 our $gcov_version_string;
106 our $graph_file_extension; 211 our $graph_file_extension;
107 our $data_file_extension; 212 our $data_file_extension;
108 our @data_directory; 213 our @data_directory;
109 our $test_name = ""; 214 our $test_name = "";
110 our $quiet; 215 our $quiet;
111 our $help; 216 our $help;
112 our $output_filename; 217 our $output_filename;
113 our $base_directory; 218 our $base_directory;
114 our $version; 219 our $version;
115 our $follow; 220 our $follow;
116 our $checksum; 221 our $checksum;
117 our $no_checksum; 222 our $no_checksum;
118 our $preserve_paths; 223 our $opt_compat_libtool;
119 our $compat_libtool; 224 our $opt_no_compat_libtool;
120 our $no_compat_libtool; 225 our $rc_adjust_src_path;# Regexp specifying parts to remove from source path
226 our $adjust_src_pattern;
227 our $adjust_src_replace;
121 our $adjust_testname; 228 our $adjust_testname;
122 our $config; # Configuration file contents 229 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) 230 our @ignore_errors; # List of errors to ignore (parameter)
126 our @ignore; # List of errors to ignore (array) 231 our @ignore; # List of errors to ignore (array)
127 our $initial; 232 our $initial;
128 our $no_recursion = 0; 233 our $no_recursion = 0;
129 our $maxdepth; 234 our $maxdepth;
235 our $no_markers = 0;
236 our $opt_derive_func_data = 0;
237 our $opt_external = 1;
238 our $opt_no_external;
239 our $debug = 0;
240 our $gcov_caps;
241 our @gcov_options;
242 our @internal_dirs;
243 our $opt_config_file;
244 our $opt_gcov_all_blocks = 1;
245 our $opt_compat;
246 our %opt_rc;
247 our %compat_value;
248 our $gcno_split_crc;
249 our $func_coverage = 1;
250 our $br_coverage = 0;
251 our $rc_auto_base = 1;
130 252
131 our $cwd = `pwd`; 253 our $cwd = `pwd`;
132 chomp($cwd); 254 chomp($cwd);
133 255
134 256
135 # 257 #
136 # Code entry point 258 # Code entry point
137 # 259 #
138 260
139 # Register handler routine to be called when interrupted 261 # Register handler routine to be called when interrupted
140 $SIG{"INT"} = \&int_handler; 262 $SIG{"INT"} = \&int_handler;
141 $SIG{__WARN__} = \&warn_handler; 263 $SIG{__WARN__} = \&warn_handler;
142 $SIG{__DIE__} = \&die_handler; 264 $SIG{__DIE__} = \&die_handler;
143 265
266 # Prettify version string
267 $lcov_version =~ s/\$\s*Revision\s*:?\s*(\S+)\s*\$/$1/;
268
269 # Set LANG so that gcov output will be in a unified format
270 $ENV{"LANG"} = "C";
271
272 # Check command line for a configuration file name
273 Getopt::Long::Configure("pass_through", "no_auto_abbrev");
274 GetOptions("config-file=s" => \$opt_config_file,
275 "rc=s%" => \%opt_rc);
276 Getopt::Long::Configure("default");
277
144 # Read configuration file if available 278 # Read configuration file if available
145 if (-r $ENV{"HOME"}."/.lcovrc") 279 if (defined($opt_config_file)) {
280 » $config = read_config($opt_config_file);
281 } elsif (defined($ENV{"HOME"}) && (-r $ENV{"HOME"}."/.lcovrc"))
146 { 282 {
147 $config = read_config($ENV{"HOME"}."/.lcovrc"); 283 $config = read_config($ENV{"HOME"}."/.lcovrc");
148 } 284 }
149 elsif (-r "/etc/lcovrc") 285 elsif (-r "/etc/lcovrc")
150 { 286 {
151 $config = read_config("/etc/lcovrc"); 287 $config = read_config("/etc/lcovrc");
152 } 288 }
153 289
154 if ($config) 290 if ($config || %opt_rc)
155 { 291 {
156 » # Copy configuration file values to variables 292 » # Copy configuration file and --rc values to variables
157 apply_config({ 293 apply_config({
158 "geninfo_gcov_tool" => \$gcov_tool, 294 "geninfo_gcov_tool" => \$gcov_tool,
159 "geninfo_adjust_testname" => \$adjust_testname, 295 "geninfo_adjust_testname" => \$adjust_testname,
160 "geninfo_checksum" => \$checksum, 296 "geninfo_checksum" => \$checksum,
161 "geninfo_no_checksum" => \$no_checksum, # deprecated 297 "geninfo_no_checksum" => \$no_checksum, # deprecated
162 » » "geninfo_compat_libtool"» => \$compat_libtool}); 298 » » "geninfo_compat_libtool"» => \$opt_compat_libtool,
299 » » "geninfo_external"» » => \$opt_external,
300 » » "geninfo_gcov_all_blocks"» => \$opt_gcov_all_blocks,
301 » » "geninfo_compat"» » => \$opt_compat,
302 » » "geninfo_adjust_src_path"» => \$rc_adjust_src_path,
303 » » "geninfo_auto_base"» » => \$rc_auto_base,
304 » » "lcov_function_coverage"» => \$func_coverage,
305 » » "lcov_branch_coverage"» » => \$br_coverage,
306 » });
163 307
164 # Merge options 308 # Merge options
165 if (defined($no_checksum)) 309 if (defined($no_checksum))
166 { 310 {
167 $checksum = ($no_checksum ? 0 : 1); 311 $checksum = ($no_checksum ? 0 : 1);
168 $no_checksum = undef; 312 $no_checksum = undef;
169 } 313 }
314
315 # Check regexp
316 if (defined($rc_adjust_src_path)) {
317 my ($pattern, $replace) = split(/\s*=>\s*/,
318 $rc_adjust_src_path);
319 local $SIG{__DIE__};
320 eval '$adjust_src_pattern = qr>'.$pattern.'>;';
321 if (!defined($adjust_src_pattern)) {
322 my $msg = $@;
323
324 chomp($msg);
325 $msg =~ s/at \(eval.*$//;
326 warn("WARNING: invalid pattern in ".
327 "geninfo_adjust_src_path: $msg\n");
328 } elsif (!defined($replace)) {
329 # If no replacement is specified, simply remove pattern
330 $adjust_src_replace = "";
331 } else {
332 $adjust_src_replace = $replace;
333 }
334 }
170 } 335 }
171 336
172 # Parse command line options 337 # Parse command line options
173 if (!GetOptions("test-name=s" => \$test_name, 338 if (!GetOptions("test-name|t=s" => \$test_name,
174 » » "output-filename=s" => \$output_filename, 339 » » "output-filename|o=s" => \$output_filename,
175 "checksum" => \$checksum, 340 "checksum" => \$checksum,
176 "no-checksum" => \$no_checksum, 341 "no-checksum" => \$no_checksum,
177 » » "base-directory=s" => \$base_directory, 342 » » "base-directory|b=s" => \$base_directory,
178 » » "version" =>\$version, 343 » » "version|v" =>\$version,
179 » » "quiet" => \$quiet, 344 » » "quiet|q" => \$quiet,
180 » » "help|?" => \$help, 345 » » "help|h|?" => \$help,
181 » » "follow" => \$follow, 346 » » "follow|f" => \$follow,
182 » » "compat-libtool" => \$compat_libtool, 347 » » "compat-libtool" => \$opt_compat_libtool,
183 » » "no-compat-libtool" => \$no_compat_libtool, 348 » » "no-compat-libtool" => \$opt_no_compat_libtool,
184 "gcov-tool=s" => \$gcov_tool, 349 "gcov-tool=s" => \$gcov_tool,
185 "ignore-errors=s" => \@ignore_errors, 350 "ignore-errors=s" => \@ignore_errors,
186 "initial|i" => \$initial, 351 "initial|i" => \$initial,
187 "no-recursion" => \$no_recursion, 352 "no-recursion" => \$no_recursion,
353 "no-markers" => \$no_markers,
354 "derive-func-data" => \$opt_derive_func_data,
355 "debug" => \$debug,
356 "external" => \$opt_external,
357 "no-external" => \$opt_no_external,
358 "compat=s" => \$opt_compat,
359 "config-file=s" => \$opt_config_file,
360 "rc=s%" => \%opt_rc,
188 )) 361 ))
189 { 362 {
190 print(STDERR "Use $tool_name --help to get usage information\n"); 363 print(STDERR "Use $tool_name --help to get usage information\n");
191 exit(1); 364 exit(1);
192 } 365 }
193 else 366 else
194 { 367 {
195 # Merge options 368 # Merge options
196 if (defined($no_checksum)) 369 if (defined($no_checksum))
197 { 370 {
198 $checksum = ($no_checksum ? 0 : 1); 371 $checksum = ($no_checksum ? 0 : 1);
199 $no_checksum = undef; 372 $no_checksum = undef;
200 } 373 }
201 374
202 » if (defined($no_compat_libtool)) 375 » if (defined($opt_no_compat_libtool))
203 { 376 {
204 » » $compat_libtool = ($no_compat_libtool ? 0 : 1); 377 » » $opt_compat_libtool = ($opt_no_compat_libtool ? 0 : 1);
205 » » $no_compat_libtool = undef; 378 » » $opt_no_compat_libtool = undef;
379 » }
380
381 » if (defined($opt_no_external)) {
382 » » $opt_external = 0;
383 » » $opt_no_external = undef;
206 } 384 }
207 } 385 }
208 386
209 @data_directory = @ARGV; 387 @data_directory = @ARGV;
210 388
211 # Check for help option 389 # Check for help option
212 if ($help) 390 if ($help)
213 { 391 {
214 print_usage(*STDOUT); 392 print_usage(*STDOUT);
215 exit(0); 393 exit(0);
216 } 394 }
217 395
218 # Check for version option 396 # Check for version option
219 if ($version) 397 if ($version)
220 { 398 {
221 print("$tool_name: $lcov_version\n"); 399 print("$tool_name: $lcov_version\n");
222 exit(0); 400 exit(0);
223 } 401 }
224 402
403 # Check gcov tool
404 if (system_no_output(3, $gcov_tool, "--help") == -1)
405 {
406 die("ERROR: need tool $gcov_tool!\n");
407 }
408
409 ($gcov_version, $gcov_version_string) = get_gcov_version();
410
411 # Determine gcov options
412 $gcov_caps = get_gcov_capabilities();
413 push(@gcov_options, "-b") if ($gcov_caps->{'branch-probabilities'} &&
414 ($br_coverage || $func_coverage));
415 push(@gcov_options, "-c") if ($gcov_caps->{'branch-counts'} &&
416 $br_coverage);
417 push(@gcov_options, "-a") if ($gcov_caps->{'all-blocks'} &&
418 $opt_gcov_all_blocks && $br_coverage);
419 push(@gcov_options, "-p") if ($gcov_caps->{'preserve-paths'});
420
421 # Determine compatibility modes
422 parse_compat_modes($opt_compat);
423
424 # Determine which errors the user wants us to ignore
425 parse_ignore_errors(@ignore_errors);
426
225 # Make sure test names only contain valid characters 427 # Make sure test names only contain valid characters
226 if ($test_name =~ s/\W/_/g) 428 if ($test_name =~ s/\W/_/g)
227 { 429 {
228 warn("WARNING: invalid characters removed from testname!\n"); 430 warn("WARNING: invalid characters removed from testname!\n");
229 } 431 }
230 432
231 # Adjust test name to include uname output if requested 433 # Adjust test name to include uname output if requested
232 if ($adjust_testname) 434 if ($adjust_testname)
233 { 435 {
234 $test_name .= "__".`uname -a`; 436 $test_name .= "__".`uname -a`;
(...skipping 21 matching lines...) Expand all
256 { 458 {
257 # Normalize to boolean 459 # Normalize to boolean
258 $checksum = ($checksum ? 1 : 0); 460 $checksum = ($checksum ? 1 : 0);
259 } 461 }
260 else 462 else
261 { 463 {
262 # Default is off 464 # Default is off
263 $checksum = 0; 465 $checksum = 0;
264 } 466 }
265 467
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 468 # Determine max depth for recursion
278 if ($no_recursion) 469 if ($no_recursion)
279 { 470 {
280 $maxdepth = "-maxdepth 1"; 471 $maxdepth = "-maxdepth 1";
281 } 472 }
282 else 473 else
283 { 474 {
284 $maxdepth = ""; 475 $maxdepth = "";
285 } 476 }
286 477
287 # Check for directory name 478 # Check for directory name
288 if (!@data_directory) 479 if (!@data_directory)
289 { 480 {
290 die("No directory specified\n". 481 die("No directory specified\n".
291 "Use $tool_name --help to get usage information\n"); 482 "Use $tool_name --help to get usage information\n");
292 } 483 }
293 else 484 else
294 { 485 {
295 foreach (@data_directory) 486 foreach (@data_directory)
296 { 487 {
297 stat($_); 488 stat($_);
298 if (!-r _) 489 if (!-r _)
299 { 490 {
300 die("ERROR: cannot read $_!\n"); 491 die("ERROR: cannot read $_!\n");
301 } 492 }
302 } 493 }
303 } 494 }
304 495
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) 496 if ($gcov_version < $GCOV_VERSION_3_4_0)
338 { 497 {
339 » if (defined($compatibility) && $compatibility eq $COMPAT_HAMMER) 498 » if (is_compat($COMPAT_MODE_HAMMER))
340 { 499 {
341 $data_file_extension = ".da"; 500 $data_file_extension = ".da";
342 $graph_file_extension = ".bbg"; 501 $graph_file_extension = ".bbg";
343 } 502 }
344 else 503 else
345 { 504 {
346 $data_file_extension = ".da"; 505 $data_file_extension = ".da";
347 $graph_file_extension = ".bb"; 506 $graph_file_extension = ".bb";
348 } 507 }
349 } 508 }
350 else 509 else
351 { 510 {
352 $data_file_extension = ".gcda"; 511 $data_file_extension = ".gcda";
353 $graph_file_extension = ".gcno"; 512 $graph_file_extension = ".gcno";
354 } 513 }
355 514
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 515 # Check output filename
363 if (defined($output_filename) && ($output_filename ne "-")) 516 if (defined($output_filename) && ($output_filename ne "-"))
364 { 517 {
365 # Initially create output filename, data is appended 518 # Initially create output filename, data is appended
366 # for each data file processed 519 # for each data file processed
367 local *DUMMY_HANDLE; 520 local *DUMMY_HANDLE;
368 » open(DUMMY_HANDLE, ">$output_filename") 521 » open(DUMMY_HANDLE, ">", $output_filename)
369 or die("ERROR: cannot create $output_filename!\n"); 522 or die("ERROR: cannot create $output_filename!\n");
370 close(DUMMY_HANDLE); 523 close(DUMMY_HANDLE);
371 524
372 # Make $output_filename an absolute path because we're going 525 # Make $output_filename an absolute path because we're going
373 # to change directories while processing files 526 # to change directories while processing files
374 if (!($output_filename =~ /^\/(.*)$/)) 527 if (!($output_filename =~ /^\/(.*)$/))
375 { 528 {
376 $output_filename = $cwd."/".$output_filename; 529 $output_filename = $cwd."/".$output_filename;
377 } 530 }
378 } 531 }
379 532
533 # Build list of directories to identify external files
534 foreach my $entry(@data_directory, $base_directory) {
535 next if (!defined($entry));
536 push(@internal_dirs, solve_relative_path($cwd, $entry));
537 }
538
380 # Do something 539 # Do something
381 if ($initial) 540 foreach my $entry (@data_directory) {
382 { 541 » gen_info($entry);
383 » foreach (@data_directory)
384 » {
385 » » gen_initial_info($_);
386 » }
387 } 542 }
388 else 543
389 { 544 if ($initial && $br_coverage) {
390 » foreach (@data_directory) 545 » warn("Note: --initial does not generate branch coverage ".
391 » { 546 » "data\n");
392 » » gen_info($_);
393 » }
394 } 547 }
395 info("Finished .info-file creation\n"); 548 info("Finished .info-file creation\n");
396 549
397 exit(0); 550 exit(0);
398 551
399 552
400 553
401 # 554 #
402 # print_usage(handle) 555 # print_usage(handle)
403 # 556 #
(...skipping 15 matching lines...) Expand all
419 -v, --version Print version number, then exit 572 -v, --version Print version number, then exit
420 -q, --quiet Do not print progress messages 573 -q, --quiet Do not print progress messages
421 -i, --initial Capture initial zero coverage data 574 -i, --initial Capture initial zero coverage data
422 -t, --test-name NAME Use test case name NAME for resulting data 575 -t, --test-name NAME Use test case name NAME for resulting data
423 -o, --output-filename OUTFILE Write data only to OUTFILE 576 -o, --output-filename OUTFILE Write data only to OUTFILE
424 -f, --follow Follow links when searching .da/.gcda files 577 -f, --follow Follow links when searching .da/.gcda files
425 -b, --base-directory DIR Use DIR as base directory for relative paths 578 -b, --base-directory DIR Use DIR as base directory for relative paths
426 --(no-)checksum Enable (disable) line checksumming 579 --(no-)checksum Enable (disable) line checksumming
427 --(no-)compat-libtool Enable (disable) libtool compatibility mode 580 --(no-)compat-libtool Enable (disable) libtool compatibility mode
428 --gcov-tool TOOL Specify gcov tool location 581 --gcov-tool TOOL Specify gcov tool location
429 --ignore-errors ERROR Continue after ERROR (gcov, source) 582 --ignore-errors ERROR Continue after ERROR (gcov, source, graph)
430 --no-recursion Exlude subdirectories from processing 583 --no-recursion Exclude subdirectories from processing
431 --function-coverage Capture function call counts 584 --no-markers Ignore exclusion markers in source code
585 --derive-func-data Generate function data from line data
586 --(no-)external Include (ignore) data for external files
587 --config-file FILENAME Specify configuration file location
588 --rc SETTING=VALUE Override configuration file setting
589 --compat MODE=on|off|auto Set compat MODE (libtool, hammer, split_crc)
432 590
433 For more information see: $lcov_url 591 For more information see: $lcov_url
434 END_OF_USAGE 592 END_OF_USAGE
435 ; 593 ;
436 } 594 }
437 595
596 #
597 # get_common_prefix(min_dir, filenames)
598 #
599 # Return the longest path prefix shared by all filenames. MIN_DIR specifies
600 # the minimum number of directories that a filename may have after removing
601 # the prefix.
602 #
603
604 sub get_common_prefix($@)
605 {
606 my ($min_dir, @files) = @_;
607 my $file;
608 my @prefix;
609 my $i;
610
611 foreach $file (@files) {
612 my ($v, $d, $f) = splitpath($file);
613 my @comp = splitdir($d);
614
615 if (!@prefix) {
616 @prefix = @comp;
617 next;
618 }
619 for ($i = 0; $i < scalar(@comp) && $i < scalar(@prefix); $i++) {
620 if ($comp[$i] ne $prefix[$i] ||
621 ((scalar(@comp) - ($i + 1)) <= $min_dir)) {
622 delete(@prefix[$i..scalar(@prefix)]);
623 last;
624 }
625 }
626 }
627
628 return catdir(@prefix);
629 }
438 630
439 # 631 #
440 # gen_info(directory) 632 # gen_info(directory)
441 # 633 #
442 # Traverse DIRECTORY and create a .info file for each data file found. 634 # Traverse DIRECTORY and create a .info file for each data file found.
443 # The .info file contains TEST_NAME in the following format: 635 # The .info file contains TEST_NAME in the following format:
444 # 636 #
445 # TN:<test name> 637 # TN:<test name>
446 # 638 #
447 # For each source file name referenced in the data file, there is a section 639 # For each source file name referenced in the data file, there is a section
(...skipping 18 matching lines...) Expand all
466 # This approach is necessary for the mechanism to work with the /proc/gcov 658 # This approach is necessary for the mechanism to work with the /proc/gcov
467 # files. 659 # files.
468 # 660 #
469 # Die on error. 661 # Die on error.
470 # 662 #
471 663
472 sub gen_info($) 664 sub gen_info($)
473 { 665 {
474 my $directory = $_[0]; 666 my $directory = $_[0];
475 my @file_list; 667 my @file_list;
668 my $file;
669 my $prefix;
670 my $type;
671 my $ext;
672
673 if ($initial) {
674 $type = "graph";
675 $ext = $graph_file_extension;
676 } else {
677 $type = "data";
678 $ext = $data_file_extension;
679 }
476 680
477 if (-d $directory) 681 if (-d $directory)
478 { 682 {
479 » » info("Scanning $directory for $data_file_extension ". 683 » » info("Scanning $directory for $ext files ...\n");
480 » » "files ...\n");»
481 684
482 » » @file_list = `find "$directory" $maxdepth $follow -name \\*$data _file_extension -type f 2>/dev/null`; 685 » » @file_list = `find "$directory" $maxdepth $follow -name \\*$ext -type f 2>/dev/null`;
483 chomp(@file_list); 686 chomp(@file_list);
484 » » @file_list or die("ERROR: no $data_file_extension files found ". 687 » » @file_list or
485 » » » » "in $directory!\n"); 688 » » » die("ERROR: no $ext files found in $directory!\n");
486 » » info("Found %d data files in %s\n", $#file_list+1, $directory); 689 » » $prefix = get_common_prefix(1, @file_list);
690 » » info("Found %d %s files in %s\n", $#file_list+1, $type,
691 » » $directory);
487 } 692 }
488 else 693 else
489 { 694 {
490 @file_list = ($directory); 695 @file_list = ($directory);
696 $prefix = "";
491 } 697 }
492 698
493 # Process all files in list 699 # Process all files in list
494 » foreach (@file_list) { process_dafile($_); } 700 » foreach $file (@file_list) {
701 » » # Process file
702 » » if ($initial) {
703 » » » process_graphfile($file, $prefix);
704 » » } else {
705 » » » process_dafile($file, $prefix);
706 » » }
707 » }
495 } 708 }
496 709
497 710
498 # 711 #
499 # process_dafile(da_filename) 712 # derive_data(contentdata, funcdata, bbdata)
713 #
714 # Calculate function coverage data by combining line coverage data and the
715 # list of lines belonging to a function.
716 #
717 # contentdata: [ instr1, count1, source1, instr2, count2, source2, ... ]
718 # instr<n>: Instrumentation flag for line n
719 # count<n>: Execution count for line n
720 # source<n>: Source code for line n
721 #
722 # funcdata: [ count1, func1, count2, func2, ... ]
723 # count<n>: Execution count for function number n
724 # func<n>: Function name for function number n
725 #
726 # bbdata: function_name -> [ line1, line2, ... ]
727 # line<n>: Line number belonging to the corresponding function
728 #
729
730 sub derive_data($$$)
731 {
732 » my ($contentdata, $funcdata, $bbdata) = @_;
733 » my @gcov_content = @{$contentdata};
734 » my @gcov_functions = @{$funcdata};
735 » my %fn_count;
736 » my %ln_fn;
737 » my $line;
738 » my $maxline;
739 » my %fn_name;
740 » my $fn;
741 » my $count;
742
743 » if (!defined($bbdata)) {
744 » » return @gcov_functions;
745 » }
746
747 » # First add existing function data
748 » while (@gcov_functions) {
749 » » $count = shift(@gcov_functions);
750 » » $fn = shift(@gcov_functions);
751
752 » » $fn_count{$fn} = $count;
753 » }
754
755 » # Convert line coverage data to function data
756 » foreach $fn (keys(%{$bbdata})) {
757 » » my $line_data = $bbdata->{$fn};
758 » » my $line;
759 » » my $fninstr = 0;
760
761 » » if ($fn eq "") {
762 » » » next;
763 » » }
764 » » # Find the lowest line count for this function
765 » » $count = 0;
766 » » foreach $line (@$line_data) {
767 » » » my $linstr = $gcov_content[ ( $line - 1 ) * 3 + 0 ];
768 » » » my $lcount = $gcov_content[ ( $line - 1 ) * 3 + 1 ];
769
770 » » » next if (!$linstr);
771 » » » $fninstr = 1;
772 » » » if (($lcount > 0) &&
773 » » » (($count == 0) || ($lcount < $count))) {
774 » » » » $count = $lcount;
775 » » » }
776 » » }
777 » » next if (!$fninstr);
778 » » $fn_count{$fn} = $count;
779 » }
780
781
782 » # Check if we got data for all functions
783 » foreach $fn (keys(%fn_name)) {
784 » » if ($fn eq "") {
785 » » » next;
786 » » }
787 » » if (defined($fn_count{$fn})) {
788 » » » next;
789 » » }
790 » » warn("WARNING: no derived data found for function $fn\n");
791 » }
792
793 » # Convert hash to list in @gcov_functions format
794 » foreach $fn (sort(keys(%fn_count))) {
795 » » push(@gcov_functions, $fn_count{$fn}, $fn);
796 » }
797
798 » return @gcov_functions;
799 }
800
801 #
802 # get_filenames(directory, pattern)
803 #
804 # Return a list of filenames found in directory which match the specified
805 # pattern.
806 #
807 # Die on error.
808 #
809
810 sub get_filenames($$)
811 {
812 » my ($dirname, $pattern) = @_;
813 » my @result;
814 » my $directory;
815 » local *DIR;
816
817 » opendir(DIR, $dirname) or
818 » » die("ERROR: cannot read directory $dirname\n");
819 » while ($directory = readdir(DIR)) {
820 » » push(@result, $directory) if ($directory =~ /$pattern/);
821 » }
822 » closedir(DIR);
823
824 » return @result;
825 }
826
827 #
828 # process_dafile(da_filename, dir)
500 # 829 #
501 # Create a .info file for a single data file. 830 # Create a .info file for a single data file.
502 # 831 #
503 # Die on error. 832 # Die on error.
504 # 833 #
505 834
506 sub process_dafile($) 835 sub process_dafile($$)
507 { 836 {
508 » info("Processing %s\n", $_[0]); 837 » my ($file, $dir) = @_;
509
510 my $da_filename; # Name of data file to process 838 my $da_filename; # Name of data file to process
511 my $da_dir; # Directory of data file 839 my $da_dir; # Directory of data file
512 my $source_dir; # Directory of source file 840 my $source_dir; # Directory of source file
513 my $da_basename; # data filename without ".da/.gcda" extension 841 my $da_basename; # data filename without ".da/.gcda" extension
514 my $bb_filename; # Name of respective graph file 842 my $bb_filename; # Name of respective graph file
515 » my %bb_content;»» # Contents of graph file 843 » my $bb_basename;» # Basename of the original graph file
844 » my $graph;» » # Contents of graph file
845 » my $instr;» » # Contents of graph file part 2
516 my $gcov_error; # Error code of gcov tool 846 my $gcov_error; # Error code of gcov tool
517 my $object_dir; # Directory containing all object files 847 my $object_dir; # Directory containing all object files
518 my $source_filename; # Name of a source code file 848 my $source_filename; # Name of a source code file
519 my $gcov_file; # Name of a .gcov file 849 my $gcov_file; # Name of a .gcov file
520 my @gcov_content; # Content of a .gcov file 850 my @gcov_content; # Content of a .gcov file
521 » my @gcov_branches;» # Branch content of a .gcov file 851 » my $gcov_branches;» # Branch content of a .gcov file
522 my @gcov_functions; # Function calls of a .gcov file 852 my @gcov_functions; # Function calls of a .gcov file
523 my @gcov_list; # List of generated .gcov files 853 my @gcov_list; # List of generated .gcov files
524 my $line_number; # Line number count 854 my $line_number; # Line number count
525 my $lines_hit; # Number of instrumented lines hit 855 my $lines_hit; # Number of instrumented lines hit
526 my $lines_found; # Number of instrumented lines found 856 my $lines_found; # Number of instrumented lines found
527 my $funcs_hit; # Number of instrumented functions hit 857 my $funcs_hit; # Number of instrumented functions hit
528 my $funcs_found; # Number of instrumented functions found 858 my $funcs_found; # Number of instrumented functions found
859 my $br_hit;
860 my $br_found;
529 my $source; # gcov source header information 861 my $source; # gcov source header information
530 my $object; # gcov object header information 862 my $object; # gcov object header information
531 my @matches; # List of absolute paths matching filename 863 my @matches; # List of absolute paths matching filename
532 my @unprocessed; # List of unprocessed source code files 864 my @unprocessed; # List of unprocessed source code files
533 my $base_dir; # Base directory for current file 865 my $base_dir; # Base directory for current file
866 my @tmp_links; # Temporary links to be cleaned up
534 my @result; 867 my @result;
535 my $index; 868 my $index;
536 my $da_renamed; # If data file is to be renamed 869 my $da_renamed; # If data file is to be renamed
537 local *INFO_HANDLE; 870 local *INFO_HANDLE;
538 871
872 info("Processing %s\n", abs2rel($file, $dir));
539 # Get path to data file in absolute and normalized form (begins with /, 873 # Get path to data file in absolute and normalized form (begins with /,
540 # contains no more ../ or ./) 874 # contains no more ../ or ./)
541 » $da_filename = solve_relative_path($cwd, $_[0]); 875 » $da_filename = solve_relative_path($cwd, $file);
542 876
543 # Get directory and basename of data file 877 # Get directory and basename of data file
544 ($da_dir, $da_basename) = split_filename($da_filename); 878 ($da_dir, $da_basename) = split_filename($da_filename);
545 879
546 » # avoid files from .libs dirs » 880 » $source_dir = $da_dir;
547 » if ($compat_libtool && $da_dir =~ m/(.*)\/\.libs$/) { 881 » if (is_compat($COMPAT_MODE_LIBTOOL)) {
548 » » $source_dir = $1; 882 » » # Avoid files from .libs dirs »
549 » } else { 883 » » $source_dir =~ s/\.libs$//;
550 » » $source_dir = $da_dir;
551 } 884 }
552 885
553 if (-z $da_filename) 886 if (-z $da_filename)
554 { 887 {
555 $da_renamed = 1; 888 $da_renamed = 1;
556 } 889 }
557 else 890 else
558 { 891 {
559 $da_renamed = 0; 892 $da_renamed = 0;
560 } 893 }
561 894
562 # Construct base_dir for current file 895 # Construct base_dir for current file
563 if ($base_directory) 896 if ($base_directory)
564 { 897 {
565 $base_dir = $base_directory; 898 $base_dir = $base_directory;
566 } 899 }
567 else 900 else
568 { 901 {
569 $base_dir = $source_dir; 902 $base_dir = $source_dir;
570 } 903 }
571 904
572 # Check for writable $base_dir (gcov will try to write files there) 905 # Check for writable $base_dir (gcov will try to write files there)
573 stat($base_dir); 906 stat($base_dir);
574 if (!-w _) 907 if (!-w _)
575 { 908 {
576 die("ERROR: cannot write to directory $base_dir!\n"); 909 die("ERROR: cannot write to directory $base_dir!\n");
577 } 910 }
578 911
579 # Construct name of graph file 912 # Construct name of graph file
580 » $bb_filename = $da_dir."/".$da_basename.$graph_file_extension; 913 » $bb_basename = $da_basename.$graph_file_extension;
914 » $bb_filename = "$da_dir/$bb_basename";
581 915
582 # Find out the real location of graph file in case we're just looking at 916 # Find out the real location of graph file in case we're just looking at
583 # a link 917 # a link
584 while (readlink($bb_filename)) 918 while (readlink($bb_filename))
585 { 919 {
586 my $last_dir = dirname($bb_filename); 920 my $last_dir = dirname($bb_filename);
587 921
588 $bb_filename = readlink($bb_filename); 922 $bb_filename = readlink($bb_filename);
589 $bb_filename = solve_relative_path($last_dir, $bb_filename); 923 $bb_filename = solve_relative_path($last_dir, $bb_filename);
590 } 924 }
591 925
592 # Ignore empty graph file (e.g. source file with no statement) 926 # Ignore empty graph file (e.g. source file with no statement)
593 if (-z $bb_filename) 927 if (-z $bb_filename)
594 { 928 {
595 warn("WARNING: empty $bb_filename (skipped)\n"); 929 warn("WARNING: empty $bb_filename (skipped)\n");
596 return; 930 return;
597 } 931 }
598 932
599 # Read contents of graph file into hash. We need it later to find out 933 # 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 934 # the absolute path to each .gcov file created as well as for
601 # information about functions and their source code positions. 935 # information about functions and their source code positions.
602 if ($gcov_version < $GCOV_VERSION_3_4_0) 936 if ($gcov_version < $GCOV_VERSION_3_4_0)
603 { 937 {
604 » » if (defined($compatibility) && $compatibility eq $COMPAT_HAMMER) 938 » » if (is_compat($COMPAT_MODE_HAMMER))
605 { 939 {
606 » » » %bb_content = read_hammer_bbg_file($bb_filename, 940 » » » ($instr, $graph) = read_bbg($bb_filename);
607 » » » » » » » $base_dir);
608 } 941 }
609 else 942 else
610 { 943 {
611 » » » %bb_content = read_bb_file($bb_filename, $base_dir); 944 » » » ($instr, $graph) = read_bb($bb_filename);
612 } 945 }
613 } 946 }
614 else 947 else
615 { 948 {
616 » » %bb_content = read_gcno_file($bb_filename, $base_dir); 949 » » ($instr, $graph) = read_gcno($bb_filename);
617 } 950 }
618 951
952 # Try to find base directory automatically if requested by user
953 if ($rc_auto_base) {
954 $base_dir = find_base_from_graph($base_dir, $instr, $graph);
955 }
956
957 ($instr, $graph) = adjust_graph_filenames($base_dir, $instr, $graph);
958
619 # Set $object_dir to real location of object files. This may differ 959 # 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 960 # from $da_dir if the graph file is just a link to the "real" object
621 # file location. 961 # file location.
622 $object_dir = dirname($bb_filename); 962 $object_dir = dirname($bb_filename);
623 963
624 # Is the data file in a different directory? (this happens e.g. with 964 # Is the data file in a different directory? (this happens e.g. with
625 # the gcov-kernel patch) 965 # the gcov-kernel patch)
626 if ($object_dir ne $da_dir) 966 if ($object_dir ne $da_dir)
627 { 967 {
628 # Need to create link to data file in $object_dir 968 # Need to create link to data file in $object_dir
629 system("ln", "-s", $da_filename, 969 system("ln", "-s", $da_filename,
630 "$object_dir/$da_basename$data_file_extension") 970 "$object_dir/$da_basename$data_file_extension")
631 and die ("ERROR: cannot create link $object_dir/". 971 and die ("ERROR: cannot create link $object_dir/".
632 "$da_basename$data_file_extension!\n"); 972 "$da_basename$data_file_extension!\n");
973 push(@tmp_links,
974 "$object_dir/$da_basename$data_file_extension");
975 # Need to create link to graph file if basename of link
976 # and file are different (CONFIG_MODVERSION compat)
977 if ((basename($bb_filename) ne $bb_basename) &&
978 (! -e "$object_dir/$bb_basename")) {
979 symlink($bb_filename, "$object_dir/$bb_basename") or
980 warn("WARNING: cannot create link ".
981 "$object_dir/$bb_basename\n");
982 push(@tmp_links, "$object_dir/$bb_basename");
983 }
633 } 984 }
634 985
635 # Change to directory containing data files and apply GCOV 986 # Change to directory containing data files and apply GCOV
987 debug("chdir($base_dir)\n");
636 chdir($base_dir); 988 chdir($base_dir);
637 989
638 if ($da_renamed) 990 if ($da_renamed)
639 { 991 {
640 # Need to rename empty data file to workaround 992 # Need to rename empty data file to workaround
641 # gcov <= 3.2.x bug (Abort) 993 # gcov <= 3.2.x bug (Abort)
642 system_no_output(3, "mv", "$da_filename", "$da_filename.ori") 994 system_no_output(3, "mv", "$da_filename", "$da_filename.ori")
643 and die ("ERROR: cannot rename $da_filename\n"); 995 and die ("ERROR: cannot rename $da_filename\n");
644 } 996 }
645 997
646 # Execute gcov command and suppress standard output 998 # Execute gcov command and suppress standard output
647 » if ($preserve_paths) 999 » $gcov_error = system_no_output(1, $gcov_tool, $da_filename,
648 » { 1000 » » » » "-o", $object_dir, @gcov_options);
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 1001
661 if ($da_renamed) 1002 if ($da_renamed)
662 { 1003 {
663 system_no_output(3, "mv", "$da_filename.ori", "$da_filename") 1004 system_no_output(3, "mv", "$da_filename.ori", "$da_filename")
664 and die ("ERROR: cannot rename $da_filename.ori"); 1005 and die ("ERROR: cannot rename $da_filename.ori");
665 } 1006 }
666 1007
667 » # Clean up link 1008 » # Clean up temporary links
668 » if ($object_dir ne $da_dir) 1009 » foreach (@tmp_links) {
669 » { 1010 » » unlink($_);
670 » » unlink($object_dir."/".$da_basename.$data_file_extension);
671 } 1011 }
672 1012
673 if ($gcov_error) 1013 if ($gcov_error)
674 { 1014 {
675 if ($ignore[$ERROR_GCOV]) 1015 if ($ignore[$ERROR_GCOV])
676 { 1016 {
677 warn("WARNING: GCOV failed for $da_filename!\n"); 1017 warn("WARNING: GCOV failed for $da_filename!\n");
678 return; 1018 return;
679 } 1019 }
680 die("ERROR: GCOV failed for $da_filename!\n"); 1020 die("ERROR: GCOV failed for $da_filename!\n");
681 } 1021 }
682 1022
683 # Collect data from resulting .gcov files and create .info file 1023 # Collect data from resulting .gcov files and create .info file
684 » @gcov_list = glob("*.gcov"); 1024 » @gcov_list = get_filenames('.', '\.gcov$');
685 1025
686 # Check for files 1026 # Check for files
687 if (!@gcov_list) 1027 if (!@gcov_list)
688 { 1028 {
689 warn("WARNING: gcov did not create any files for ". 1029 warn("WARNING: gcov did not create any files for ".
690 "$da_filename!\n"); 1030 "$da_filename!\n");
691 } 1031 }
692 1032
693 # Check whether we're writing to a single file 1033 # Check whether we're writing to a single file
694 if ($output_filename) 1034 if ($output_filename)
695 { 1035 {
696 if ($output_filename eq "-") 1036 if ($output_filename eq "-")
697 { 1037 {
698 *INFO_HANDLE = *STDOUT; 1038 *INFO_HANDLE = *STDOUT;
699 } 1039 }
700 else 1040 else
701 { 1041 {
702 # Append to output file 1042 # Append to output file
703 » » » open(INFO_HANDLE, ">>$output_filename") 1043 » » » open(INFO_HANDLE, ">>", $output_filename)
704 or die("ERROR: cannot write to ". 1044 or die("ERROR: cannot write to ".
705 "$output_filename!\n"); 1045 "$output_filename!\n");
706 } 1046 }
707 } 1047 }
708 else 1048 else
709 { 1049 {
710 # Open .info file for output 1050 # Open .info file for output
711 » » open(INFO_HANDLE, ">$da_filename.info") 1051 » » open(INFO_HANDLE, ">", "$da_filename.info")
712 or die("ERROR: cannot create $da_filename.info!\n"); 1052 or die("ERROR: cannot create $da_filename.info!\n");
713 } 1053 }
714 1054
715 # Write test name 1055 # Write test name
716 printf(INFO_HANDLE "TN:%s\n", $test_name); 1056 printf(INFO_HANDLE "TN:%s\n", $test_name);
717 1057
718 # Traverse the list of generated .gcov files and combine them into a 1058 # Traverse the list of generated .gcov files and combine them into a
719 # single .info file 1059 # single .info file
720 » @unprocessed = keys(%bb_content); 1060 » @unprocessed = keys(%{$instr});
721 » foreach $gcov_file (@gcov_list) 1061 » foreach $gcov_file (sort(@gcov_list))
722 { 1062 {
1063 my $i;
1064 my $num;
1065
1066 # Skip gcov file for gcc built-in code
1067 next if ($gcov_file eq "<built-in>.gcov");
1068
723 ($source, $object) = read_gcov_header($gcov_file); 1069 ($source, $object) = read_gcov_header($gcov_file);
724 1070
725 » » if (defined($source)) 1071 » » if (!defined($source)) {
726 » » { 1072 » » » # Derive source file name from gcov file name if
727 » » » $source = solve_relative_path($base_dir, $source); 1073 » » » # header format could not be parsed
1074 » » » $source = $gcov_file;
1075 » » » $source =~ s/\.gcov$//;
1076 » » }
1077
1078 » » $source = solve_relative_path($base_dir, $source);
1079
1080 » » if (defined($adjust_src_pattern)) {
1081 » » » # Apply transformation as specified by user
1082 » » » $source =~ s/$adjust_src_pattern/$adjust_src_replace/g;
728 } 1083 }
729 1084
730 # gcov will happily create output even if there's no source code 1085 # gcov will happily create output even if there's no source code
731 # available - this interferes with checksum creation so we need 1086 # available - this interferes with checksum creation so we need
732 # to pull the emergency brake here. 1087 # to pull the emergency brake here.
733 » » if (defined($source) && ! -r $source && $checksum) 1088 » » if (! -r $source && $checksum)
734 { 1089 {
735 if ($ignore[$ERROR_SOURCE]) 1090 if ($ignore[$ERROR_SOURCE])
736 { 1091 {
737 warn("WARNING: could not read source file ". 1092 warn("WARNING: could not read source file ".
738 "$source\n"); 1093 "$source\n");
739 next; 1094 next;
740 } 1095 }
741 die("ERROR: could not read source file $source\n"); 1096 die("ERROR: could not read source file $source\n");
742 } 1097 }
743 1098
744 » » @matches = match_filename(defined($source) ? $source : 1099 » » @matches = match_filename($source, keys(%{$instr}));
745 » » » » » $gcov_file, keys(%bb_content));
746 1100
747 # Skip files that are not mentioned in the graph file 1101 # Skip files that are not mentioned in the graph file
748 if (!@matches) 1102 if (!@matches)
749 { 1103 {
750 warn("WARNING: cannot find an entry for ".$gcov_file. 1104 warn("WARNING: cannot find an entry for ".$gcov_file.
751 " in $graph_file_extension file, skipping ". 1105 " in $graph_file_extension file, skipping ".
752 "file!\n"); 1106 "file!\n");
753 unlink($gcov_file); 1107 unlink($gcov_file);
754 next; 1108 next;
755 } 1109 }
756 1110
757 # Read in contents of gcov file 1111 # Read in contents of gcov file
758 @result = read_gcov_file($gcov_file); 1112 @result = read_gcov_file($gcov_file);
1113 if (!defined($result[0])) {
1114 warn("WARNING: skipping unreadable file ".
1115 $gcov_file."\n");
1116 unlink($gcov_file);
1117 next;
1118 }
759 @gcov_content = @{$result[0]}; 1119 @gcov_content = @{$result[0]};
760 » » @gcov_branches = @{$result[1]}; 1120 » » $gcov_branches = $result[1];
761 @gcov_functions = @{$result[2]}; 1121 @gcov_functions = @{$result[2]};
762 1122
763 # Skip empty files 1123 # Skip empty files
764 if (!@gcov_content) 1124 if (!@gcov_content)
765 { 1125 {
766 warn("WARNING: skipping empty file ".$gcov_file."\n"); 1126 warn("WARNING: skipping empty file ".$gcov_file."\n");
767 unlink($gcov_file); 1127 unlink($gcov_file);
768 next; 1128 next;
769 } 1129 }
770 1130
(...skipping 12 matching lines...) Expand all
783 # Remove processed file from list 1143 # Remove processed file from list
784 for ($index = scalar(@unprocessed) - 1; $index >= 0; $index--) 1144 for ($index = scalar(@unprocessed) - 1; $index >= 0; $index--)
785 { 1145 {
786 if ($unprocessed[$index] eq $source_filename) 1146 if ($unprocessed[$index] eq $source_filename)
787 { 1147 {
788 splice(@unprocessed, $index, 1); 1148 splice(@unprocessed, $index, 1);
789 last; 1149 last;
790 } 1150 }
791 } 1151 }
792 1152
1153 # Skip external files if requested
1154 if (!$opt_external) {
1155 if (is_external($source_filename)) {
1156 info(" ignoring data for external file ".
1157 "$source_filename\n");
1158 unlink($gcov_file);
1159 next;
1160 }
1161 }
1162
793 # Write absolute path of source file 1163 # Write absolute path of source file
794 printf(INFO_HANDLE "SF:%s\n", $source_filename); 1164 printf(INFO_HANDLE "SF:%s\n", $source_filename);
795 1165
1166 # If requested, derive function coverage data from
1167 # line coverage data of the first line of a function
1168 if ($opt_derive_func_data) {
1169 @gcov_functions =
1170 derive_data(\@gcov_content, \@gcov_functions,
1171 $graph->{$source_filename});
1172 }
1173
796 # Write function-related information 1174 # Write function-related information
797 » » if (defined($bb_content{$source_filename})) 1175 » » if (defined($graph->{$source_filename}))
798 { 1176 {
799 » » » foreach (split(",",$bb_content{$source_filename})) 1177 » » » my $fn_data = $graph->{$source_filename};
800 » » » { 1178 » » » my $fn;
801 » » » » my ($fn, $line) = split("=", $_);
802 1179
1180 foreach $fn (sort
1181 {$fn_data->{$a}->[0] <=> $fn_data->{$b}->[0]}
1182 keys(%{$fn_data})) {
1183 my $ln_data = $fn_data->{$fn};
1184 my $line = $ln_data->[0];
1185
1186 # Skip empty function
803 if ($fn eq "") { 1187 if ($fn eq "") {
804 next; 1188 next;
805 } 1189 }
1190 # Remove excluded functions
1191 if (!$no_markers) {
1192 my $gfn;
1193 my $found = 0;
1194
1195 foreach $gfn (@gcov_functions) {
1196 if ($gfn eq $fn) {
1197 $found = 1;
1198 last;
1199 }
1200 }
1201 if (!$found) {
1202 next;
1203 }
1204 }
806 1205
807 # Normalize function name 1206 # Normalize function name
808 » » » » $fn =~ s/\W/_/g; 1207 » » » » $fn = filter_fn_name($fn);
809 1208
810 print(INFO_HANDLE "FN:$line,$fn\n"); 1209 print(INFO_HANDLE "FN:$line,$fn\n");
811 } 1210 }
812 } 1211 }
813 1212
814 #-- 1213 #--
815 #-- FNDA: <call-count>, <function-name> 1214 #-- FNDA: <call-count>, <function-name>
816 #-- FNF: overall count of functions 1215 #-- FNF: overall count of functions
817 #-- FNH: overall count of functions with non-zero call count 1216 #-- FNH: overall count of functions with non-zero call count
818 #-- 1217 #--
819 $funcs_found = 0; 1218 $funcs_found = 0;
820 $funcs_hit = 0; 1219 $funcs_hit = 0;
821 while (@gcov_functions) 1220 while (@gcov_functions)
822 { 1221 {
823 » » » printf(INFO_HANDLE "FNDA:%s,%s\n", 1222 » » » my $count = shift(@gcov_functions);
824 » » » » $gcov_functions[0], 1223 » » » my $fn = shift(@gcov_functions);
825 » » » » $gcov_functions[1]); 1224
826 » » » » $funcs_found++; 1225 » » » $fn = filter_fn_name($fn);
827 » » » $funcs_hit++ if $gcov_functions[0]; 1226 » » » printf(INFO_HANDLE "FNDA:$count,$fn\n");
828 » » » splice(@gcov_functions,0,2); 1227 » » » $funcs_found++;
1228 » » » $funcs_hit++ if ($count > 0);
829 } 1229 }
830 if ($funcs_found > 0) { 1230 if ($funcs_found > 0) {
831 printf(INFO_HANDLE "FNF:%s\n", $funcs_found); 1231 printf(INFO_HANDLE "FNF:%s\n", $funcs_found);
832 printf(INFO_HANDLE "FNH:%s\n", $funcs_hit); 1232 printf(INFO_HANDLE "FNH:%s\n", $funcs_hit);
833 } 1233 }
834 1234
1235 # Write coverage information for each instrumented branch:
1236 #
1237 # BRDA:<line number>,<block number>,<branch number>,<taken>
1238 #
1239 # where 'taken' is the number of times the branch was taken
1240 # or '-' if the block to which the branch belongs was never
1241 # executed
1242 $br_found = 0;
1243 $br_hit = 0;
1244 $num = br_gvec_len($gcov_branches);
1245 for ($i = 0; $i < $num; $i++) {
1246 my ($line, $block, $branch, $taken) =
1247 br_gvec_get($gcov_branches, $i);
1248
1249 print(INFO_HANDLE "BRDA:$line,$block,$branch,$taken\n");
1250 $br_found++;
1251 $br_hit++ if ($taken ne '-' && $taken > 0);
1252 }
1253 if ($br_found > 0) {
1254 printf(INFO_HANDLE "BRF:%s\n", $br_found);
1255 printf(INFO_HANDLE "BRH:%s\n", $br_hit);
1256 }
1257
835 # Reset line counters 1258 # Reset line counters
836 $line_number = 0; 1259 $line_number = 0;
837 $lines_found = 0; 1260 $lines_found = 0;
838 $lines_hit = 0; 1261 $lines_hit = 0;
839 1262
840 # Write coverage information for each instrumented line 1263 # Write coverage information for each instrumented line
841 # Note: @gcov_content contains a list of (flag, count, source) 1264 # Note: @gcov_content contains a list of (flag, count, source)
842 # tuple for each source code line 1265 # tuple for each source code line
843 while (@gcov_content) 1266 while (@gcov_content)
844 { 1267 {
(...skipping 10 matching lines...) Expand all
855 1278
856 # Increase $lines_hit in case of an execution 1279 # Increase $lines_hit in case of an execution
857 # count>0 1280 # count>0
858 if ($gcov_content[1] > 0) { $lines_hit++; } 1281 if ($gcov_content[1] > 0) { $lines_hit++; }
859 } 1282 }
860 1283
861 # Remove already processed data from array 1284 # Remove already processed data from array
862 splice(@gcov_content,0,3); 1285 splice(@gcov_content,0,3);
863 } 1286 }
864 1287
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 1288 # Write line statistics and section separator
887 printf(INFO_HANDLE "LF:%s\n", $lines_found); 1289 printf(INFO_HANDLE "LF:%s\n", $lines_found);
888 printf(INFO_HANDLE "LH:%s\n", $lines_hit); 1290 printf(INFO_HANDLE "LH:%s\n", $lines_hit);
889 print(INFO_HANDLE "end_of_record\n"); 1291 print(INFO_HANDLE "end_of_record\n");
890 1292
891 # Remove .gcov file after processing 1293 # Remove .gcov file after processing
892 unlink($gcov_file); 1294 unlink($gcov_file);
893 } 1295 }
894 1296
895 # Check for files which show up in the graph file but were never 1297 # Check for files which show up in the graph file but were never
(...skipping 19 matching lines...) Expand all
915 # 1317 #
916 # solve_relative_path(path, dir) 1318 # solve_relative_path(path, dir)
917 # 1319 #
918 # Solve relative path components of DIR which, if not absolute, resides in PATH. 1320 # Solve relative path components of DIR which, if not absolute, resides in PATH.
919 # 1321 #
920 1322
921 sub solve_relative_path($$) 1323 sub solve_relative_path($$)
922 { 1324 {
923 my $path = $_[0]; 1325 my $path = $_[0];
924 my $dir = $_[1]; 1326 my $dir = $_[1];
1327 my $volume;
1328 my $directories;
1329 my $filename;
1330 my @dirs; # holds path elements
925 my $result; 1331 my $result;
926 1332
1333 # Convert from Windows path to msys path
1334 if( $^O eq "msys" )
1335 {
1336 # search for a windows drive letter at the beginning
1337 ($volume, $directories, $filename) = File::Spec::Win32->splitpat h( $dir );
1338 if( $volume ne '' )
1339 {
1340 my $uppercase_volume;
1341 # transform c/d\../e/f\g to Windows style c\d\..\e\f\g
1342 $dir = File::Spec::Win32->canonpath( $dir );
1343 # use Win32 module to retrieve path components
1344 # $uppercase_volume is not used any further
1345 ( $uppercase_volume, $directories, $filename ) = File::S pec::Win32->splitpath( $dir );
1346 @dirs = File::Spec::Win32->splitdir( $directories );
1347
1348 # prepend volume, since in msys C: is always mounted to /c
1349 $volume =~ s|^([a-zA-Z]+):|/\L$1\E|;
1350 unshift( @dirs, $volume );
1351
1352 # transform to Unix style '/' path
1353 $directories = File::Spec->catdir( @dirs );
1354 $dir = File::Spec->catpath( '', $directories, $filename );
1355 } else {
1356 # eliminate '\' path separators
1357 $dir = File::Spec->canonpath( $dir );
1358 }
1359 }
1360
927 $result = $dir; 1361 $result = $dir;
928 # Prepend path if not absolute 1362 # Prepend path if not absolute
929 if ($dir =~ /^[^\/]/) 1363 if ($dir =~ /^[^\/]/)
930 { 1364 {
931 $result = "$path/$result"; 1365 $result = "$path/$result";
932 } 1366 }
933 1367
934 # Remove // 1368 # Remove //
935 $result =~ s/\/\//\//g; 1369 $result =~ s/\/\//\//g;
936 1370
937 # Remove . 1371 # Remove .
938 $result =~ s/\/\.\//\//g; 1372 $result =~ s/\/\.\//\//g;
1373 $result =~ s/\/\.$/\//g;
1374
1375 # Remove trailing /
1376 $result =~ s/\/$//g;
939 1377
940 # Solve .. 1378 # Solve ..
941 while ($result =~ s/\/[^\/]+\/\.\.\//\//) 1379 while ($result =~ s/\/[^\/]+\/\.\.\//\//)
942 { 1380 {
943 } 1381 }
944 1382
945 # Remove preceding .. 1383 # Remove preceding ..
946 $result =~ s/^\/\.\.\//\//g; 1384 $result =~ s/^\/\.\.\//\//g;
947 1385
948 return $result; 1386 return $result;
949 } 1387 }
950 1388
951 1389
952 # 1390 #
953 # match_filename(gcov_filename, list) 1391 # match_filename(gcov_filename, list)
954 # 1392 #
955 # Return a list of those entries of LIST which match the relative filename 1393 # Return a list of those entries of LIST which match the relative filename
956 # GCOV_FILENAME. 1394 # GCOV_FILENAME.
957 # 1395 #
958 1396
959 sub match_filename($@) 1397 sub match_filename($@)
960 { 1398 {
961 » my $filename = shift; 1399 » my ($filename, @list) = @_;
962 » my @list = @_; 1400 » my ($vol, $dir, $file) = splitpath($filename);
1401 » my @comp = splitdir($dir);
1402 » my $comps = scalar(@comp);
1403 » my $entry;
963 my @result; 1404 my @result;
964 1405
965 » $filename =~ s/^(.*).gcov$/$1/; 1406 entry:
1407 » foreach $entry (@list) {
1408 » » my ($evol, $edir, $efile) = splitpath($entry);
1409 » » my @ecomp;
1410 » » my $ecomps;
1411 » » my $i;
966 1412
967 » if ($filename =~ /^\/(.*)$/) 1413 » » # Filename component must match
968 » { 1414 » » if ($efile ne $file) {
969 » » $filename = "$1"; 1415 » » » next;
1416 » » }
1417 » » # Check directory components last to first for match
1418 » » @ecomp = splitdir($edir);
1419 » » $ecomps = scalar(@ecomp);
1420 » » if ($ecomps < $comps) {
1421 » » » next;
1422 » » }
1423 » » for ($i = 0; $i < $comps; $i++) {
1424 » » » if ($comp[$comps - $i - 1] ne
1425 » » » $ecomp[$ecomps - $i - 1]) {
1426 » » » » next entry;
1427 » » » }
1428 » » }
1429 » » push(@result, $entry),
970 } 1430 }
971 1431
972 foreach (@list)
973 {
974 if (/\/\Q$filename\E(.*)$/ && $1 eq "")
975 {
976 @result = (@result, $_);
977 }
978 }
979 return @result; 1432 return @result;
980 } 1433 }
981 1434
982
983 # 1435 #
984 # solve_ambiguous_match(rel_filename, matches_ref, gcov_content_ref) 1436 # solve_ambiguous_match(rel_filename, matches_ref, gcov_content_ref)
985 # 1437 #
986 # Try to solve ambiguous matches of mapping (gcov file) -> (source code) file 1438 # 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 1439 # 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 1440 # in MATCHES. REL_FILENAME identifies the relative filename of the gcov
989 # file. 1441 # file.
990 # 1442 #
991 # Return the one real match or die if there is none. 1443 # Return the one real match or die if there is none.
992 # 1444 #
993 1445
994 sub solve_ambiguous_match($$$) 1446 sub solve_ambiguous_match($$$)
995 { 1447 {
996 my $rel_name = $_[0]; 1448 my $rel_name = $_[0];
997 my $matches = $_[1]; 1449 my $matches = $_[1];
998 my $content = $_[2]; 1450 my $content = $_[2];
999 my $filename; 1451 my $filename;
1000 my $index; 1452 my $index;
1001 my $no_match; 1453 my $no_match;
1002 local *SOURCE; 1454 local *SOURCE;
1003 1455
1004 # Check the list of matches 1456 # Check the list of matches
1005 foreach $filename (@$matches) 1457 foreach $filename (@$matches)
1006 { 1458 {
1007 1459
1008 # Compare file contents 1460 # Compare file contents
1009 » » open(SOURCE, $filename) 1461 » » open(SOURCE, "<", $filename)
1010 or die("ERROR: cannot read $filename!\n"); 1462 or die("ERROR: cannot read $filename!\n");
1011 1463
1012 $no_match = 0; 1464 $no_match = 0;
1013 for ($index = 2; <SOURCE>; $index += 3) 1465 for ($index = 2; <SOURCE>; $index += 3)
1014 { 1466 {
1015 chomp; 1467 chomp;
1016 1468
1469 # Also remove CR from line-end
1470 s/\015$//;
1471
1017 if ($_ ne @$content[$index]) 1472 if ($_ ne @$content[$index])
1018 { 1473 {
1019 $no_match = 1; 1474 $no_match = 1;
1020 last; 1475 last;
1021 } 1476 }
1022 } 1477 }
1023 1478
1024 close(SOURCE); 1479 close(SOURCE);
1025 1480
1026 if (!$no_match) 1481 if (!$no_match)
(...skipping 18 matching lines...) Expand all
1045 my @path_components = split('/', $_[0]); 1500 my @path_components = split('/', $_[0]);
1046 my @file_components = split('\.', pop(@path_components)); 1501 my @file_components = split('\.', pop(@path_components));
1047 my $extension = pop(@file_components); 1502 my $extension = pop(@file_components);
1048 1503
1049 return (join("/",@path_components), join(".",@file_components), 1504 return (join("/",@path_components), join(".",@file_components),
1050 $extension); 1505 $extension);
1051 } 1506 }
1052 1507
1053 1508
1054 # 1509 #
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) 1510 # read_gcov_header(gcov_filename)
1071 # 1511 #
1072 # Parse file GCOV_FILENAME and return a list containing the following 1512 # Parse file GCOV_FILENAME and return a list containing the following
1073 # information: 1513 # information:
1074 # 1514 #
1075 # (source, object) 1515 # (source, object)
1076 # 1516 #
1077 # where: 1517 # where:
1078 # 1518 #
1079 # source: complete relative path of the source code file (gcc >= 3.3 only) 1519 # source: complete relative path of the source code file (gcc >= 3.3 only)
1080 # object: name of associated graph file 1520 # object: name of associated graph file
1081 # 1521 #
1082 # Die on error. 1522 # Die on error.
1083 # 1523 #
1084 1524
1085 sub read_gcov_header($) 1525 sub read_gcov_header($)
1086 { 1526 {
1087 my $source; 1527 my $source;
1088 my $object; 1528 my $object;
1089 local *INPUT; 1529 local *INPUT;
1090 1530
1091 » if (!open(INPUT, $_[0])) 1531 » if (!open(INPUT, "<", $_[0]))
1092 { 1532 {
1093 if ($ignore_errors[$ERROR_GCOV]) 1533 if ($ignore_errors[$ERROR_GCOV])
1094 { 1534 {
1095 warn("WARNING: cannot read $_[0]!\n"); 1535 warn("WARNING: cannot read $_[0]!\n");
1096 return (undef,undef); 1536 return (undef,undef);
1097 } 1537 }
1098 die("ERROR: cannot read $_[0]!\n"); 1538 die("ERROR: cannot read $_[0]!\n");
1099 } 1539 }
1100 1540
1101 while (<INPUT>) 1541 while (<INPUT>)
1102 { 1542 {
1103 chomp($_); 1543 chomp($_);
1104 1544
1545 # Also remove CR from line-end
1546 s/\015$//;
1547
1105 if (/^\s+-:\s+0:Source:(.*)$/) 1548 if (/^\s+-:\s+0:Source:(.*)$/)
1106 { 1549 {
1107 # Source: header entry 1550 # Source: header entry
1108 $source = $1; 1551 $source = $1;
1109 } 1552 }
1110 elsif (/^\s+-:\s+0:Object:(.*)$/) 1553 elsif (/^\s+-:\s+0:Object:(.*)$/)
1111 { 1554 {
1112 # Object: header entry 1555 # Object: header entry
1113 $object = $1; 1556 $object = $1;
1114 } 1557 }
1115 else 1558 else
1116 { 1559 {
1117 last; 1560 last;
1118 } 1561 }
1119 } 1562 }
1120 1563
1121 close(INPUT); 1564 close(INPUT);
1122 1565
1123 return ($source, $object); 1566 return ($source, $object);
1124 } 1567 }
1125 1568
1126 1569
1127 # 1570 #
1571 # br_gvec_len(vector)
1572 #
1573 # Return the number of entries in the branch coverage vector.
1574 #
1575
1576 sub br_gvec_len($)
1577 {
1578 my ($vec) = @_;
1579
1580 return 0 if (!defined($vec));
1581 return (length($vec) * 8 / $BR_VEC_WIDTH) / $BR_VEC_ENTRIES;
1582 }
1583
1584
1585 #
1586 # br_gvec_get(vector, number)
1587 #
1588 # Return an entry from the branch coverage vector.
1589 #
1590
1591 sub br_gvec_get($$)
1592 {
1593 my ($vec, $num) = @_;
1594 my $line;
1595 my $block;
1596 my $branch;
1597 my $taken;
1598 my $offset = $num * $BR_VEC_ENTRIES;
1599
1600 # Retrieve data from vector
1601 $line = vec($vec, $offset + $BR_LINE, $BR_VEC_WIDTH);
1602 $block = vec($vec, $offset + $BR_BLOCK, $BR_VEC_WIDTH);
1603 $branch = vec($vec, $offset + $BR_BRANCH, $BR_VEC_WIDTH);
1604 $taken = vec($vec, $offset + $BR_TAKEN, $BR_VEC_WIDTH);
1605
1606 # Decode taken value from an integer
1607 if ($taken == 0) {
1608 $taken = "-";
1609 } else {
1610 $taken--;
1611 }
1612
1613 return ($line, $block, $branch, $taken);
1614 }
1615
1616
1617 #
1618 # br_gvec_push(vector, line, block, branch, taken)
1619 #
1620 # Add an entry to the branch coverage vector.
1621 #
1622
1623 sub br_gvec_push($$$$$)
1624 {
1625 my ($vec, $line, $block, $branch, $taken) = @_;
1626 my $offset;
1627
1628 $vec = "" if (!defined($vec));
1629 $offset = br_gvec_len($vec) * $BR_VEC_ENTRIES;
1630
1631 # Encode taken value into an integer
1632 if ($taken eq "-") {
1633 $taken = 0;
1634 } else {
1635 $taken++;
1636 }
1637
1638 # Add to vector
1639 vec($vec, $offset + $BR_LINE, $BR_VEC_WIDTH) = $line;
1640 vec($vec, $offset + $BR_BLOCK, $BR_VEC_WIDTH) = $block;
1641 vec($vec, $offset + $BR_BRANCH, $BR_VEC_WIDTH) = $branch;
1642 vec($vec, $offset + $BR_TAKEN, $BR_VEC_WIDTH) = $taken;
1643
1644 return $vec;
1645 }
1646
1647
1648 #
1128 # read_gcov_file(gcov_filename) 1649 # read_gcov_file(gcov_filename)
1129 # 1650 #
1130 # Parse file GCOV_FILENAME (.gcov file format) and return the list: 1651 # Parse file GCOV_FILENAME (.gcov file format) and return the list:
1131 # (reference to gcov_content, reference to gcov_branch, reference to gcov_func) 1652 # (reference to gcov_content, reference to gcov_branch, reference to gcov_func)
1132 # 1653 #
1133 # gcov_content is a list of 3 elements 1654 # gcov_content is a list of 3 elements
1134 # (flag, count, source) for each source code line: 1655 # (flag, count, source) for each source code line:
1135 # 1656 #
1136 # $result[($line_number-1)*3+0] = instrumentation flag for line $line_number 1657 # $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 1658 # $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 1659 # $result[($line_number-1)*3+2] = source code text for line $line_number
1139 # 1660 #
1140 # gcov_branch is a list of 2 elements 1661 # gcov_branch is a vector of 4 4-byte long elements for each branch:
1141 # (linenumber, branch result) for each branch 1662 # line number, block number, branch number, count + 1 or 0
1142 # 1663 #
1143 # gcov_func is a list of 2 elements 1664 # gcov_func is a list of 2 elements
1144 # (number of calls, function name) for each function 1665 # (number of calls, function name) for each function
1145 # 1666 #
1146 # Die on error. 1667 # Die on error.
1147 # 1668 #
1148 1669
1149 sub read_gcov_file($) 1670 sub read_gcov_file($)
1150 { 1671 {
1151 my $filename = $_[0]; 1672 my $filename = $_[0];
1152 my @result = (); 1673 my @result = ();
1153 » my @branches = (); 1674 » my $branches = "";
1154 my @functions = (); 1675 my @functions = ();
1155 my $number; 1676 my $number;
1677 my $exclude_flag = 0;
1678 my $exclude_line = 0;
1679 my $last_block = $UNNAMED_BLOCK;
1680 my $last_line = 0;
1156 local *INPUT; 1681 local *INPUT;
1157 1682
1158 » open(INPUT, $filename) 1683 » if (!open(INPUT, "<", $filename)) {
1159 » » or die("ERROR: cannot read $filename!\n"); 1684 » » if ($ignore_errors[$ERROR_GCOV])
1685 » » {
1686 » » » warn("WARNING: cannot read $filename!\n");
1687 » » » return (undef, undef, undef);
1688 » » }
1689 » » die("ERROR: cannot read $filename!\n");
1690 » }
1160 1691
1161 if ($gcov_version < $GCOV_VERSION_3_3_0) 1692 if ($gcov_version < $GCOV_VERSION_3_3_0)
1162 { 1693 {
1163 # Expect gcov format as used in gcc < 3.3 1694 # Expect gcov format as used in gcc < 3.3
1164 while (<INPUT>) 1695 while (<INPUT>)
1165 { 1696 {
1166 chomp($_); 1697 chomp($_);
1167 1698
1168 » » » if (/^\t\t(.*)$/) 1699 » » » # Also remove CR from line-end
1169 » » » { 1700 » » » s/\015$//;
1170 » » » » # Uninstrumented line 1701
1171 » » » » push(@result, 0); 1702 » » » if (/^branch\s+(\d+)\s+taken\s+=\s+(\d+)/) {
1172 » » » » push(@result, 0); 1703 » » » » next if (!$br_coverage);
1173 » » » » push(@result, $1); 1704 » » » » next if ($exclude_line);
1174 » » » } 1705 » » » » $branches = br_gvec_push($branches, $last_line,
1175 » » » elsif (/^branch/) 1706 » » » » » » $last_block, $1, $2);
1176 » » » { 1707 » » » } elsif (/^branch\s+(\d+)\s+never\s+executed/) {
1177 » » » » # Branch execution data 1708 » » » » next if (!$br_coverage);
1178 » » » » push(@branches, scalar(@result) / 3); 1709 » » » » next if ($exclude_line);
1179 » » » » if (/^branch \d+ never executed$/) 1710 » » » » $branches = br_gvec_push($branches, $last_line,
1180 » » » » { 1711 » » » » » » $last_block, $1, '-');
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 } 1712 }
1192 elsif (/^call/ || /^function/) 1713 elsif (/^call/ || /^function/)
1193 { 1714 {
1194 # Function call return data 1715 # Function call return data
1195 } 1716 }
1196 else 1717 else
1197 { 1718 {
1719 $last_line++;
1720 # Check for exclusion markers
1721 if (!$no_markers) {
1722 if (/$EXCL_STOP/) {
1723 $exclude_flag = 0;
1724 } elsif (/$EXCL_START/) {
1725 $exclude_flag = 1;
1726 }
1727 if (/$EXCL_LINE/ || $exclude_flag) {
1728 $exclude_line = 1;
1729 } else {
1730 $exclude_line = 0;
1731 }
1732 }
1198 # Source code execution data 1733 # Source code execution data
1734 if (/^\t\t(.*)$/)
1735 {
1736 # Uninstrumented line
1737 push(@result, 0);
1738 push(@result, 0);
1739 push(@result, $1);
1740 next;
1741 }
1199 $number = (split(" ",substr($_, 0, 16)))[0]; 1742 $number = (split(" ",substr($_, 0, 16)))[0];
1200 1743
1201 # Check for zero count which is indicated 1744 # Check for zero count which is indicated
1202 # by ###### 1745 # by ######
1203 if ($number eq "######") { $number = 0; } 1746 if ($number eq "######") { $number = 0; }
1204 1747
1205 » » » » push(@result, 1); 1748 » » » » if ($exclude_line) {
1206 » » » » push(@result, $number); 1749 » » » » » # Register uninstrumented line instead
1750 » » » » » push(@result, 0);
1751 » » » » » push(@result, 0);
1752 » » » » } else {
1753 » » » » » push(@result, 1);
1754 » » » » » push(@result, $number);
1755 » » » » }
1207 push(@result, substr($_, 16)); 1756 push(@result, substr($_, 16));
1208 } 1757 }
1209 } 1758 }
1210 } 1759 }
1211 else 1760 else
1212 { 1761 {
1213 # Expect gcov format as used in gcc >= 3.3 1762 # Expect gcov format as used in gcc >= 3.3
1214 while (<INPUT>) 1763 while (<INPUT>)
1215 { 1764 {
1216 chomp($_); 1765 chomp($_);
1217 1766
1218 » » » if (/^branch\s+\d+\s+(\S+)\s+(\S+)/) 1767 » » » # Also remove CR from line-end
1768 » » » s/\015$//;
1769
1770 » » » if (/^\s*(\d+|\$+):\s*(\d+)-block\s+(\d+)\s*$/) {
1771 » » » » # Block information - used to group related
1772 » » » » # branches
1773 » » » » $last_line = $2;
1774 » » » » $last_block = $3;
1775 » » » } elsif (/^branch\s+(\d+)\s+taken\s+(\d+)/) {
1776 » » » » next if (!$br_coverage);
1777 » » » » next if ($exclude_line);
1778 » » » » $branches = br_gvec_push($branches, $last_line,
1779 » » » » » » $last_block, $1, $2);
1780 » » » } elsif (/^branch\s+(\d+)\s+never\s+executed/) {
1781 » » » » next if (!$br_coverage);
1782 » » » » next if ($exclude_line);
1783 » » » » $branches = br_gvec_push($branches, $last_line,
1784 » » » » » » $last_block, $1, '-');
1785 » » » }
1786 » » » elsif (/^function\s+(.+)\s+called\s+(\d+)\s+/)
1219 { 1787 {
1220 » » » » # Branch execution data 1788 » » » » next if (!$func_coverage);
1221 » » » » push(@branches, scalar(@result) / 3); 1789 » » » » if ($exclude_line) {
1222 » » » » if ($1 eq "never") 1790 » » » » » next;
1223 » » » » {
1224 » » » » » push(@branches, 0);
1225 } 1791 }
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); 1792 push(@functions, $2, $1);
1238 } 1793 }
1239 elsif (/^call/) 1794 elsif (/^call/)
1240 { 1795 {
1241 # Function call return data 1796 # Function call return data
1242 } 1797 }
1243 elsif (/^\s*([^:]+):\s*([^:]+):(.*)$/) 1798 elsif (/^\s*([^:]+):\s*([^:]+):(.*)$/)
1244 { 1799 {
1800 my ($count, $line, $code) = ($1, $2, $3);
1801
1802 $last_line = $line;
1803 $last_block = $UNNAMED_BLOCK;
1804 # Check for exclusion markers
1805 if (!$no_markers) {
1806 if (/$EXCL_STOP/) {
1807 $exclude_flag = 0;
1808 } elsif (/$EXCL_START/) {
1809 $exclude_flag = 1;
1810 }
1811 if (/$EXCL_LINE/ || $exclude_flag) {
1812 $exclude_line = 1;
1813 } else {
1814 $exclude_line = 0;
1815 }
1816 }
1245 # <exec count>:<line number>:<source code> 1817 # <exec count>:<line number>:<source code>
1246 » » » » if ($2 eq "0") 1818 » » » » if ($line eq "0")
1247 { 1819 {
1248 # Extra data 1820 # Extra data
1249 } 1821 }
1250 » » » » elsif ($1 eq "-") 1822 » » » » elsif ($count eq "-")
1251 { 1823 {
1252 # Uninstrumented line 1824 # Uninstrumented line
1253 push(@result, 0); 1825 push(@result, 0);
1254 push(@result, 0); 1826 push(@result, 0);
1255 » » » » » push(@result, $3); 1827 » » » » » push(@result, $code);
1256 } 1828 }
1257 else 1829 else
1258 { 1830 {
1259 » » » » » # Source code execution data 1831 » » » » » if ($exclude_line) {
1260 » » » » » $number = $1; 1832 » » » » » » push(@result, 0);
1261 1833 » » » » » » push(@result, 0);
1262 » » » » » # Check for zero count 1834 » » » » » } else {
1263 » » » » » if ($number eq "#####")»{ $number = 0; } 1835 » » » » » » # Check for zero count
1264 1836 » » » » » » if ($count eq "#####") {
1265 » » » » » push(@result, 1); 1837 » » » » » » » $count = 0;
1266 » » » » » push(@result, $number); 1838 » » » » » » }
1267 » » » » » push(@result, $3); 1839 » » » » » » push(@result, 1);
1840 » » » » » » push(@result, $count);
1841 » » » » » }
1842 » » » » » push(@result, $code);
1268 } 1843 }
1269 } 1844 }
1270 } 1845 }
1271 } 1846 }
1272 1847
1273 close(INPUT); 1848 close(INPUT);
1274 » return(\@result, \@branches, \@functions); 1849 » if ($exclude_flag) {
1850 » » warn("WARNING: unterminated exclusion section in $filename\n");
1851 » }
1852 » return(\@result, $branches, \@functions);
1275 } 1853 }
1276 1854
1277 1855
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 # 1856 #
1823 # Get the GCOV tool version. Return an integer number which represents the 1857 # Get the GCOV tool version. Return an integer number which represents the
1824 # GCOV version. Version numbers can be compared using standard integer 1858 # GCOV version. Version numbers can be compared using standard integer
1825 # operations. 1859 # operations.
1826 # 1860 #
1827 1861
1828 sub get_gcov_version() 1862 sub get_gcov_version()
1829 { 1863 {
1830 local *HANDLE; 1864 local *HANDLE;
1831 my $version_string; 1865 my $version_string;
1832 my $result; 1866 my $result;
1833 1867
1834 » open(GCOV_PIPE, "$gcov_tool -v |") 1868 » open(GCOV_PIPE, "-|", "$gcov_tool -v")
1835 or die("ERROR: cannot retrieve gcov version!\n"); 1869 or die("ERROR: cannot retrieve gcov version!\n");
1836 $version_string = <GCOV_PIPE>; 1870 $version_string = <GCOV_PIPE>;
1837 close(GCOV_PIPE); 1871 close(GCOV_PIPE);
1838 1872
1839 $result = 0; 1873 $result = 0;
1840 if ($version_string =~ /(\d+)\.(\d+)(\.(\d+))?/) 1874 if ($version_string =~ /(\d+)\.(\d+)(\.(\d+))?/)
1841 { 1875 {
1842 if (defined($4)) 1876 if (defined($4))
1843 { 1877 {
1844 info("Found gcov version: $1.$2.$4\n"); 1878 info("Found gcov version: $1.$2.$4\n");
1845 $result = $1 << 16 | $2 << 8 | $4; 1879 $result = $1 << 16 | $2 << 8 | $4;
1846 } 1880 }
1847 else 1881 else
1848 { 1882 {
1849 info("Found gcov version: $1.$2\n"); 1883 info("Found gcov version: $1.$2\n");
1850 $result = $1 << 16 | $2 << 8; 1884 $result = $1 << 16 | $2 << 8;
1851 } 1885 }
1852 } 1886 }
1853 if ($version_string =~ /suse/i && $result == 0x30303 || 1887 » return ($result, $version_string);
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 } 1888 }
1861 1889
1862 1890
1863 # 1891 #
1864 # info(printf_parameter) 1892 # info(printf_parameter)
1865 # 1893 #
1866 # Use printf to write PRINTF_PARAMETER to stdout only when the $quiet flag 1894 # Use printf to write PRINTF_PARAMETER to stdout only when the $quiet flag
1867 # is not set. 1895 # is not set.
1868 # 1896 #
1869 1897
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
1912 # 1940 #
1913 1941
1914 sub system_no_output($@) 1942 sub system_no_output($@)
1915 { 1943 {
1916 my $mode = shift; 1944 my $mode = shift;
1917 my $result; 1945 my $result;
1918 local *OLD_STDERR; 1946 local *OLD_STDERR;
1919 local *OLD_STDOUT; 1947 local *OLD_STDOUT;
1920 1948
1921 # Save old stdout and stderr handles 1949 # Save old stdout and stderr handles
1922 » ($mode & 1) && open(OLD_STDOUT, ">>&STDOUT"); 1950 » ($mode & 1) && open(OLD_STDOUT, ">>&", "STDOUT");
1923 » ($mode & 2) && open(OLD_STDERR, ">>&STDERR"); 1951 » ($mode & 2) && open(OLD_STDERR, ">>&", "STDERR");
1924 1952
1925 # Redirect to /dev/null 1953 # Redirect to /dev/null
1926 » ($mode & 1) && open(STDOUT, ">/dev/null"); 1954 » ($mode & 1) && open(STDOUT, ">", "/dev/null");
1927 » ($mode & 2) && open(STDERR, ">/dev/null"); 1955 » ($mode & 2) && open(STDERR, ">", "/dev/null");
1928 1956
1957 debug("system(".join(' ', @_).")\n");
1929 system(@_); 1958 system(@_);
1930 $result = $?; 1959 $result = $?;
1931 1960
1932 # Close redirected handles 1961 # Close redirected handles
1933 ($mode & 1) && close(STDOUT); 1962 ($mode & 1) && close(STDOUT);
1934 ($mode & 2) && close(STDERR); 1963 ($mode & 2) && close(STDERR);
1935 1964
1936 # Restore old handles 1965 # Restore old handles
1937 » ($mode & 1) && open(STDOUT, ">>&OLD_STDOUT"); 1966 » ($mode & 1) && open(STDOUT, ">>&", "OLD_STDOUT");
1938 » ($mode & 2) && open(STDERR, ">>&OLD_STDERR"); 1967 » ($mode & 2) && open(STDERR, ">>&", "OLD_STDERR");
1939 1968
1940 return $result; 1969 return $result;
1941 } 1970 }
1942 1971
1943 1972
1944 # 1973 #
1945 # read_config(filename) 1974 # read_config(filename)
1946 # 1975 #
1947 # Read configuration file FILENAME and return a reference to a hash containing 1976 # Read configuration file FILENAME and return a reference to a hash containing
1948 # all valid key=value pairs found. 1977 # all valid key=value pairs found.
1949 # 1978 #
1950 1979
1951 sub read_config($) 1980 sub read_config($)
1952 { 1981 {
1953 my $filename = $_[0]; 1982 my $filename = $_[0];
1954 my %result; 1983 my %result;
1955 my $key; 1984 my $key;
1956 my $value; 1985 my $value;
1957 local *HANDLE; 1986 local *HANDLE;
1958 1987
1959 » if (!open(HANDLE, "<$filename")) 1988 » if (!open(HANDLE, "<", $filename))
1960 { 1989 {
1961 warn("WARNING: cannot read configuration file $filename\n"); 1990 warn("WARNING: cannot read configuration file $filename\n");
1962 return undef; 1991 return undef;
1963 } 1992 }
1964 while (<HANDLE>) 1993 while (<HANDLE>)
1965 { 1994 {
1966 chomp; 1995 chomp;
1967 # Skip comments 1996 # Skip comments
1968 s/#.*//; 1997 s/#.*//;
1969 # Remove leading blanks 1998 # Remove leading blanks
(...skipping 18 matching lines...) Expand all
1988 2017
1989 2018
1990 # 2019 #
1991 # apply_config(REF) 2020 # apply_config(REF)
1992 # 2021 #
1993 # REF is a reference to a hash containing the following mapping: 2022 # REF is a reference to a hash containing the following mapping:
1994 # 2023 #
1995 # key_string => var_ref 2024 # key_string => var_ref
1996 # 2025 #
1997 # where KEY_STRING is a keyword and VAR_REF is a reference to an associated 2026 # 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 2027 # variable. If the global configuration hashes CONFIG or OPT_RC contain a value
1999 # keyword KEY_STRING, VAR_REF will be assigned the value for that keyword. 2028 # for keyword KEY_STRING, VAR_REF will be assigned the value for that keyword.
2000 # 2029 #
2001 2030
2002 sub apply_config($) 2031 sub apply_config($)
2003 { 2032 {
2004 my $ref = $_[0]; 2033 my $ref = $_[0];
2005 2034
2006 foreach (keys(%{$ref})) 2035 foreach (keys(%{$ref}))
2007 { 2036 {
2008 » » if (defined($config->{$_})) 2037 » » if (defined($opt_rc{$_})) {
2009 » » { 2038 » » » ${$ref->{$_}} = $opt_rc{$_};
2039 » » } elsif (defined($config->{$_})) {
2010 ${$ref->{$_}} = $config->{$_}; 2040 ${$ref->{$_}} = $config->{$_};
2011 } 2041 }
2012 } 2042 }
2013 } 2043 }
2014 2044
2015 2045
2016 sub gen_initial_info($) 2046 #
2047 # get_exclusion_data(filename)
2048 #
2049 # Scan specified source code file for exclusion markers and return
2050 # linenumber -> 1
2051 # for all lines which should be excluded.
2052 #
2053
2054 sub get_exclusion_data($)
2017 { 2055 {
2018 » my $directory = $_[0]; 2056 » my ($filename) = @_;
2019 » my @file_list; 2057 » my %list;
2058 » my $flag = 0;
2059 » local *HANDLE;
2020 2060
2021 » if (-d $directory) 2061 » if (!open(HANDLE, "<", $filename)) {
2022 » { 2062 » » warn("WARNING: could not open $filename\n");
2023 » » info("Scanning $directory for $graph_file_extension ". 2063 » » return undef;
2024 » » "files ...\n");» 2064 » }
2065 » while (<HANDLE>) {
2066 » » if (/$EXCL_STOP/) {
2067 » » » $flag = 0;
2068 » » } elsif (/$EXCL_START/) {
2069 » » » $flag = 1;
2070 » » }
2071 » » if (/$EXCL_LINE/ || $flag) {
2072 » » » $list{$.} = 1;
2073 » » }
2074 » }
2075 » close(HANDLE);
2025 2076
2026 » » @file_list = `find "$directory" $maxdepth $follow -name \\*$grap h_file_extension -type f 2>/dev/null`; 2077 » if ($flag) {
2027 » » chomp(@file_list); 2078 » » warn("WARNING: unterminated exclusion section in $filename\n");
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 } 2079 }
2036 2080
2037 » # Process all files in list 2081 » return \%list;
2038 » foreach (@file_list) { process_graphfile($_); }
2039 } 2082 }
2040 2083
2041 sub process_graphfile($) 2084
2085 #
2086 # apply_exclusion_data(instr, graph)
2087 #
2088 # Remove lines from instr and graph data structures which are marked
2089 # for exclusion in the source code file.
2090 #
2091 # Return adjusted (instr, graph).
2092 #
2093 # graph : file name -> function data
2094 # function data : function name -> line data
2095 # line data : [ line1, line2, ... ]
2096 #
2097 # instr : filename -> line data
2098 # line data : [ line1, line2, ... ]
2099 #
2100
2101 sub apply_exclusion_data($$)
2042 { 2102 {
2043 » my $graph_filename = $_[0]; 2103 » my ($instr, $graph) = @_;
2104 » my $filename;
2105 » my %excl_data;
2106 » my $excl_read_failed = 0;
2107
2108 » # Collect exclusion marker data
2109 » foreach $filename (sort_uniq_lex(keys(%{$graph}), keys(%{$instr}))) {
2110 » » my $excl = get_exclusion_data($filename);
2111
2112 » » # Skip and note if file could not be read
2113 » » if (!defined($excl)) {
2114 » » » $excl_read_failed = 1;
2115 » » » next;
2116 » » }
2117
2118 » » # Add to collection if there are markers
2119 » » $excl_data{$filename} = $excl if (keys(%{$excl}) > 0);
2120 » }
2121
2122 » # Warn if not all source files could be read
2123 » if ($excl_read_failed) {
2124 » » warn("WARNING: some exclusion markers may be ignored\n");
2125 » }
2126
2127 » # Skip if no markers were found
2128 » return ($instr, $graph) if (keys(%excl_data) == 0);
2129
2130 » # Apply exclusion marker data to graph
2131 » foreach $filename (keys(%excl_data)) {
2132 » » my $function_data = $graph->{$filename};
2133 » » my $excl = $excl_data{$filename};
2134 » » my $function;
2135
2136 » » next if (!defined($function_data));
2137
2138 » » foreach $function (keys(%{$function_data})) {
2139 » » » my $line_data = $function_data->{$function};
2140 » » » my $line;
2141 » » » my @new_data;
2142
2143 » » » # To be consistent with exclusion parser in non-initial
2144 » » » # case we need to remove a function if the first line
2145 » » » # was excluded
2146 » » » if ($excl->{$line_data->[0]}) {
2147 » » » » delete($function_data->{$function});
2148 » » » » next;
2149 » » » }
2150 » » » # Copy only lines which are not excluded
2151 » » » foreach $line (@{$line_data}) {
2152 » » » » push(@new_data, $line) if (!$excl->{$line});
2153 » » » }
2154
2155 » » » # Store modified list
2156 » » » if (scalar(@new_data) > 0) {
2157 » » » » $function_data->{$function} = \@new_data;
2158 » » » } else {
2159 » » » » # All of this function was excluded
2160 » » » » delete($function_data->{$function});
2161 » » » }
2162 » » }
2163
2164 » » # Check if all functions of this file were excluded
2165 » » if (keys(%{$function_data}) == 0) {
2166 » » » delete($graph->{$filename});
2167 » » }
2168 » }
2169
2170 » # Apply exclusion marker data to instr
2171 » foreach $filename (keys(%excl_data)) {
2172 » » my $line_data = $instr->{$filename};
2173 » » my $excl = $excl_data{$filename};
2174 » » my $line;
2175 » » my @new_data;
2176
2177 » » next if (!defined($line_data));
2178
2179 » » # Copy only lines which are not excluded
2180 » » foreach $line (@{$line_data}) {
2181 » » » push(@new_data, $line) if (!$excl->{$line});
2182 » » }
2183
2184 » » # Store modified list
2185 » » $instr->{$filename} = \@new_data;
2186 » }
2187
2188 » return ($instr, $graph);
2189 }
2190
2191
2192 sub process_graphfile($$)
2193 {
2194 » my ($file, $dir) = @_;
2195 » my $graph_filename = $file;
2044 my $graph_dir; 2196 my $graph_dir;
2045 my $graph_basename; 2197 my $graph_basename;
2046 my $source_dir; 2198 my $source_dir;
2047 my $base_dir; 2199 my $base_dir;
2048 » my %graph_data; 2200 » my $graph;
2201 » my $instr;
2049 my $filename; 2202 my $filename;
2050 local *INFO_HANDLE; 2203 local *INFO_HANDLE;
2051 2204
2052 » info("Processing $_[0]\n"); 2205 » info("Processing %s\n", abs2rel($file, $dir));
2053 2206
2054 # Get path to data file in absolute and normalized form (begins with /, 2207 # Get path to data file in absolute and normalized form (begins with /,
2055 # contains no more ../ or ./) 2208 # contains no more ../ or ./)
2056 $graph_filename = solve_relative_path($cwd, $graph_filename); 2209 $graph_filename = solve_relative_path($cwd, $graph_filename);
2057 2210
2058 # Get directory and basename of data file 2211 # Get directory and basename of data file
2059 ($graph_dir, $graph_basename) = split_filename($graph_filename); 2212 ($graph_dir, $graph_basename) = split_filename($graph_filename);
2060 2213
2061 » # avoid files from .libs dirs » 2214 » $source_dir = $graph_dir;
2062 » if ($compat_libtool && $graph_dir =~ m/(.*)\/\.libs$/) { 2215 » if (is_compat($COMPAT_MODE_LIBTOOL)) {
2063 » » $source_dir = $1; 2216 » » # Avoid files from .libs dirs »
2064 » } else { 2217 » » $source_dir =~ s/\.libs$//;
2065 » » $source_dir = $graph_dir;
2066 } 2218 }
2067 2219
2068 # Construct base_dir for current file 2220 # Construct base_dir for current file
2069 if ($base_directory) 2221 if ($base_directory)
2070 { 2222 {
2071 $base_dir = $base_directory; 2223 $base_dir = $base_directory;
2072 } 2224 }
2073 else 2225 else
2074 { 2226 {
2075 $base_dir = $source_dir; 2227 $base_dir = $source_dir;
2076 } 2228 }
2077 2229
2078 if ($gcov_version < $GCOV_VERSION_3_4_0) 2230 if ($gcov_version < $GCOV_VERSION_3_4_0)
2079 { 2231 {
2080 » » if (defined($compatibility) && $compatibility eq $COMPAT_HAMMER) 2232 » » if (is_compat($COMPAT_MODE_HAMMER))
2081 { 2233 {
2082 » » » %graph_data = read_hammer_bbg_file($graph_filename, 2234 » » » ($instr, $graph) = read_bbg($graph_filename);
2083 » » » » » » » $base_dir);
2084 } 2235 }
2085 else 2236 else
2086 { 2237 {
2087 » » » %graph_data = read_bb_file($graph_filename, $base_dir); 2238 » » » ($instr, $graph) = read_bb($graph_filename);
2088 } 2239 }
2089 } 2240 }
2090 else 2241 else
2091 { 2242 {
2092 » » %graph_data = read_gcno_file($graph_filename, $base_dir); 2243 » » ($instr, $graph) = read_gcno($graph_filename);
2244 » }
2245
2246 » # Try to find base directory automatically if requested by user
2247 » if ($rc_auto_base) {
2248 » » $base_dir = find_base_from_graph($base_dir, $instr, $graph);
2249 » }
2250
2251 » ($instr, $graph) = adjust_graph_filenames($base_dir, $instr, $graph);
2252
2253 » if (!$no_markers) {
2254 » » # Apply exclusion marker data to graph file data
2255 » » ($instr, $graph) = apply_exclusion_data($instr, $graph);
2093 } 2256 }
2094 2257
2095 # Check whether we're writing to a single file 2258 # Check whether we're writing to a single file
2096 if ($output_filename) 2259 if ($output_filename)
2097 { 2260 {
2098 if ($output_filename eq "-") 2261 if ($output_filename eq "-")
2099 { 2262 {
2100 *INFO_HANDLE = *STDOUT; 2263 *INFO_HANDLE = *STDOUT;
2101 } 2264 }
2102 else 2265 else
2103 { 2266 {
2104 # Append to output file 2267 # Append to output file
2105 » » » open(INFO_HANDLE, ">>$output_filename") 2268 » » » open(INFO_HANDLE, ">>", $output_filename)
2106 or die("ERROR: cannot write to ". 2269 or die("ERROR: cannot write to ".
2107 "$output_filename!\n"); 2270 "$output_filename!\n");
2108 } 2271 }
2109 } 2272 }
2110 else 2273 else
2111 { 2274 {
2112 # Open .info file for output 2275 # Open .info file for output
2113 » » open(INFO_HANDLE, ">$graph_filename.info") 2276 » » open(INFO_HANDLE, ">", "$graph_filename.info")
2114 or die("ERROR: cannot create $graph_filename.info!\n"); 2277 or die("ERROR: cannot create $graph_filename.info!\n");
2115 } 2278 }
2116 2279
2117 # Write test name 2280 # Write test name
2118 printf(INFO_HANDLE "TN:%s\n", $test_name); 2281 printf(INFO_HANDLE "TN:%s\n", $test_name);
2119 » foreach $filename (keys(%graph_data)) 2282 » foreach $filename (sort(keys(%{$instr})))
2120 { 2283 {
2121 » » my %lines; 2284 » » my $funcdata = $graph->{$filename};
2122 » » my $count = 0; 2285 » » my $line;
2123 » » my @functions; 2286 » » my $linedata;
2124 2287
2125 print(INFO_HANDLE "SF:$filename\n"); 2288 print(INFO_HANDLE "SF:$filename\n");
2126 2289
2127 » » # Write function related data 2290 » » if (defined($funcdata) && $func_coverage) {
2128 » » foreach (split(",",$graph_data{$filename})) 2291 » » » my @functions = sort {$funcdata->{$a}->[0] <=>
2129 » » { 2292 » » » » » $funcdata->{$b}->[0]}
2130 » » » my ($fn, $line) = split("=", $_); 2293 » » » » » keys(%{$funcdata});
2294 » » » my $func;
2131 2295
2132 » » » if ($fn eq "") 2296 » » » # Gather list of instrumented lines and functions
2133 » » » { 2297 » » » foreach $func (@functions) {
2134 » » » » $lines{$line} = ""; 2298 » » » » $linedata = $funcdata->{$func};
2135 » » » » next; 2299
2300 » » » » # Print function name and starting line
2301 » » » » print(INFO_HANDLE "FN:".$linedata->[0].
2302 » » » » ",".filter_fn_name($func)."\n");
2136 } 2303 }
2304 # Print zero function coverage data
2305 foreach $func (@functions) {
2306 print(INFO_HANDLE "FNDA:0,".
2307 filter_fn_name($func)."\n");
2308 }
2309 # Print function summary
2310 print(INFO_HANDLE "FNF:".scalar(@functions)."\n");
2311 print(INFO_HANDLE "FNH:0\n");
2312 }
2313 # Print zero line coverage data
2314 foreach $line (@{$instr->{$filename}}) {
2315 print(INFO_HANDLE "DA:$line,0\n");
2316 }
2317 # Print line summary
2318 print(INFO_HANDLE "LF:".scalar(@{$instr->{$filename}})."\n");
2319 print(INFO_HANDLE "LH:0\n");
2137 2320
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"); 2321 print(INFO_HANDLE "end_of_record\n");
2159 } 2322 }
2160 if (!($output_filename && ($output_filename eq "-"))) 2323 if (!($output_filename && ($output_filename eq "-")))
2161 { 2324 {
2162 close(INFO_HANDLE); 2325 close(INFO_HANDLE);
2163 } 2326 }
2164 } 2327 }
2165 2328
2329 sub filter_fn_name($)
2330 {
2331 my ($fn) = @_;
2332
2333 # Remove characters used internally as function name delimiters
2334 $fn =~ s/[,=]/_/g;
2335
2336 return $fn;
2337 }
2338
2166 sub warn_handler($) 2339 sub warn_handler($)
2167 { 2340 {
2168 my ($msg) = @_; 2341 my ($msg) = @_;
2169 2342
2170 warn("$tool_name: $msg"); 2343 warn("$tool_name: $msg");
2171 } 2344 }
2172 2345
2173 sub die_handler($) 2346 sub die_handler($)
2174 { 2347 {
2175 my ($msg) = @_; 2348 my ($msg) = @_;
2176 2349
2177 die("$tool_name: $msg"); 2350 die("$tool_name: $msg");
2178 } 2351 }
2352
2353
2354 #
2355 # graph_error(filename, message)
2356 #
2357 # Print message about error in graph file. If ignore_graph_error is set, return.
2358 # Otherwise abort.
2359 #
2360
2361 sub graph_error($$)
2362 {
2363 my ($filename, $msg) = @_;
2364
2365 if ($ignore[$ERROR_GRAPH]) {
2366 warn("WARNING: $filename: $msg - skipping\n");
2367 return;
2368 }
2369 die("ERROR: $filename: $msg\n");
2370 }
2371
2372 #
2373 # graph_expect(description)
2374 #
2375 # If debug is set to a non-zero value, print the specified description of what
2376 # is expected to be read next from the graph file.
2377 #
2378
2379 sub graph_expect($)
2380 {
2381 my ($msg) = @_;
2382
2383 if (!$debug || !defined($msg)) {
2384 return;
2385 }
2386
2387 print(STDERR "DEBUG: expecting $msg\n");
2388 }
2389
2390 #
2391 # graph_read(handle, bytes[, description, peek])
2392 #
2393 # Read and return the specified number of bytes from handle. Return undef
2394 # if the number of bytes could not be read. If PEEK is non-zero, reset
2395 # file position after read.
2396 #
2397
2398 sub graph_read(*$;$$)
2399 {
2400 my ($handle, $length, $desc, $peek) = @_;
2401 my $data;
2402 my $result;
2403 my $pos;
2404
2405 graph_expect($desc);
2406 if ($peek) {
2407 $pos = tell($handle);
2408 if ($pos == -1) {
2409 warn("Could not get current file position: $!\n");
2410 return undef;
2411 }
2412 }
2413 $result = read($handle, $data, $length);
2414 if ($debug) {
2415 my $op = $peek ? "peek" : "read";
2416 my $ascii = "";
2417 my $hex = "";
2418 my $i;
2419
2420 print(STDERR "DEBUG: $op($length)=$result: ");
2421 for ($i = 0; $i < length($data); $i++) {
2422 my $c = substr($data, $i, 1);;
2423 my $n = ord($c);
2424
2425 $hex .= sprintf("%02x ", $n);
2426 if ($n >= 32 && $n <= 127) {
2427 $ascii .= $c;
2428 } else {
2429 $ascii .= ".";
2430 }
2431 }
2432 print(STDERR "$hex |$ascii|");
2433 print(STDERR "\n");
2434 }
2435 if ($peek) {
2436 if (!seek($handle, $pos, 0)) {
2437 warn("Could not set file position: $!\n");
2438 return undef;
2439 }
2440 }
2441 if ($result != $length) {
2442 return undef;
2443 }
2444 return $data;
2445 }
2446
2447 #
2448 # graph_skip(handle, bytes[, description])
2449 #
2450 # Read and discard the specified number of bytes from handle. Return non-zero
2451 # if bytes could be read, zero otherwise.
2452 #
2453
2454 sub graph_skip(*$;$)
2455 {
2456 my ($handle, $length, $desc) = @_;
2457
2458 if (defined(graph_read($handle, $length, $desc))) {
2459 return 1;
2460 }
2461 return 0;
2462 }
2463
2464 #
2465 # sort_uniq(list)
2466 #
2467 # Return list in numerically ascending order and without duplicate entries.
2468 #
2469
2470 sub sort_uniq(@)
2471 {
2472 my (@list) = @_;
2473 my %hash;
2474
2475 foreach (@list) {
2476 $hash{$_} = 1;
2477 }
2478 return sort { $a <=> $b } keys(%hash);
2479 }
2480
2481 #
2482 # sort_uniq_lex(list)
2483 #
2484 # Return list in lexically ascending order and without duplicate entries.
2485 #
2486
2487 sub sort_uniq_lex(@)
2488 {
2489 my (@list) = @_;
2490 my %hash;
2491
2492 foreach (@list) {
2493 $hash{$_} = 1;
2494 }
2495 return sort keys(%hash);
2496 }
2497
2498 #
2499 # parent_dir(dir)
2500 #
2501 # Return parent directory for DIR. DIR must not contain relative path
2502 # components.
2503 #
2504
2505 sub parent_dir($)
2506 {
2507 my ($dir) = @_;
2508 my ($v, $d, $f) = splitpath($dir, 1);
2509 my @dirs = splitdir($d);
2510
2511 pop(@dirs);
2512
2513 return catpath($v, catdir(@dirs), $f);
2514 }
2515
2516 #
2517 # find_base_from_graph(base_dir, instr, graph)
2518 #
2519 # Try to determine the base directory of the graph file specified by INSTR
2520 # and GRAPH. The base directory is the base for all relative filenames in
2521 # the graph file. It is defined by the current working directory at time
2522 # of compiling the source file.
2523 #
2524 # This function implements a heuristic which relies on the following
2525 # assumptions:
2526 # - all files used for compilation are still present at their location
2527 # - the base directory is either BASE_DIR or one of its parent directories
2528 # - files by the same name are not present in multiple parent directories
2529 #
2530
2531 sub find_base_from_graph($$$)
2532 {
2533 my ($base_dir, $instr, $graph) = @_;
2534 my $old_base;
2535 my $best_miss;
2536 my $best_base;
2537 my %rel_files;
2538
2539 # Determine list of relative paths
2540 foreach my $filename (keys(%{$instr}), keys(%{$graph})) {
2541 next if (file_name_is_absolute($filename));
2542
2543 $rel_files{$filename} = 1;
2544 }
2545
2546 # Early exit if there are no relative paths
2547 return $base_dir if (!%rel_files);
2548
2549 do {
2550 my $miss = 0;
2551
2552 foreach my $filename (keys(%rel_files)) {
2553 if (!-e solve_relative_path($base_dir, $filename)) {
2554 $miss++;
2555 }
2556 }
2557
2558 debug("base_dir=$base_dir miss=$miss\n");
2559
2560 # Exit if we find an exact match with no misses
2561 return $base_dir if ($miss == 0);
2562
2563 # No exact match, aim for the one with the least source file
2564 # misses
2565 if (!defined($best_base) || $miss < $best_miss) {
2566 $best_base = $base_dir;
2567 $best_miss = $miss;
2568 }
2569
2570 # Repeat until there's no more parent directory
2571 $old_base = $base_dir;
2572 $base_dir = parent_dir($base_dir);
2573 } while ($old_base ne $base_dir);
2574
2575 return $best_base;
2576 }
2577
2578 #
2579 # adjust_graph_filenames(base_dir, instr, graph)
2580 #
2581 # Make relative paths in INSTR and GRAPH absolute and apply
2582 # geninfo_adjust_src_path setting to graph file data.
2583 #
2584
2585 sub adjust_graph_filenames($$$)
2586 {
2587 my ($base_dir, $instr, $graph) = @_;
2588
2589 foreach my $filename (keys(%{$instr})) {
2590 my $old_filename = $filename;
2591
2592 # Convert to absolute canonical form
2593 $filename = solve_relative_path($base_dir, $filename);
2594
2595 # Apply adjustment
2596 if (defined($adjust_src_pattern)) {
2597 $filename =~ s/$adjust_src_pattern/$adjust_src_replace/g ;
2598 }
2599
2600 if ($filename ne $old_filename) {
2601 $instr->{$filename} = delete($instr->{$old_filename});
2602 }
2603 }
2604
2605 foreach my $filename (keys(%{$graph})) {
2606 my $old_filename = $filename;
2607
2608 # Make absolute
2609 # Convert to absolute canonical form
2610 $filename = solve_relative_path($base_dir, $filename);
2611
2612 # Apply adjustment
2613 if (defined($adjust_src_pattern)) {
2614 $filename =~ s/$adjust_src_pattern/$adjust_src_replace/g ;
2615 }
2616
2617 if ($filename ne $old_filename) {
2618 $graph->{$filename} = delete($graph->{$old_filename});
2619 }
2620 }
2621
2622 return ($instr, $graph);
2623 }
2624
2625 #
2626 # graph_cleanup(graph)
2627 #
2628 # Remove entries for functions with no lines. Remove duplicate line numbers.
2629 # Sort list of line numbers numerically ascending.
2630 #
2631
2632 sub graph_cleanup($)
2633 {
2634 my ($graph) = @_;
2635 my $filename;
2636
2637 foreach $filename (keys(%{$graph})) {
2638 my $per_file = $graph->{$filename};
2639 my $function;
2640
2641 foreach $function (keys(%{$per_file})) {
2642 my $lines = $per_file->{$function};
2643
2644 if (scalar(@$lines) == 0) {
2645 # Remove empty function
2646 delete($per_file->{$function});
2647 next;
2648 }
2649 # Normalize list
2650 $per_file->{$function} = [ sort_uniq(@$lines) ];
2651 }
2652 if (scalar(keys(%{$per_file})) == 0) {
2653 # Remove empty file
2654 delete($graph->{$filename});
2655 }
2656 }
2657 }
2658
2659 #
2660 # graph_find_base(bb)
2661 #
2662 # Try to identify the filename which is the base source file for the
2663 # specified bb data.
2664 #
2665
2666 sub graph_find_base($)
2667 {
2668 my ($bb) = @_;
2669 my %file_count;
2670 my $basefile;
2671 my $file;
2672 my $func;
2673 my $filedata;
2674 my $count;
2675 my $num;
2676
2677 # Identify base name for this bb data.
2678 foreach $func (keys(%{$bb})) {
2679 $filedata = $bb->{$func};
2680
2681 foreach $file (keys(%{$filedata})) {
2682 $count = $file_count{$file};
2683
2684 # Count file occurrence
2685 $file_count{$file} = defined($count) ? $count + 1 : 1;
2686 }
2687 }
2688 $count = 0;
2689 $num = 0;
2690 foreach $file (keys(%file_count)) {
2691 if ($file_count{$file} > $count) {
2692 # The file that contains code for the most functions
2693 # is likely the base file
2694 $count = $file_count{$file};
2695 $num = 1;
2696 $basefile = $file;
2697 } elsif ($file_count{$file} == $count) {
2698 # If more than one file could be the basefile, we
2699 # don't have a basefile
2700 $basefile = undef;
2701 }
2702 }
2703
2704 return $basefile;
2705 }
2706
2707 #
2708 # graph_from_bb(bb, fileorder, bb_filename)
2709 #
2710 # Convert data from bb to the graph format and list of instrumented lines.
2711 # Returns (instr, graph).
2712 #
2713 # bb : function name -> file data
2714 # : undef -> file order
2715 # file data : filename -> line data
2716 # line data : [ line1, line2, ... ]
2717 #
2718 # file order : function name -> [ filename1, filename2, ... ]
2719 #
2720 # graph : file name -> function data
2721 # function data : function name -> line data
2722 # line data : [ line1, line2, ... ]
2723 #
2724 # instr : filename -> line data
2725 # line data : [ line1, line2, ... ]
2726 #
2727
2728 sub graph_from_bb($$$)
2729 {
2730 my ($bb, $fileorder, $bb_filename) = @_;
2731 my $graph = {};
2732 my $instr = {};
2733 my $basefile;
2734 my $file;
2735 my $func;
2736 my $filedata;
2737 my $linedata;
2738 my $order;
2739
2740 $basefile = graph_find_base($bb);
2741 # Create graph structure
2742 foreach $func (keys(%{$bb})) {
2743 $filedata = $bb->{$func};
2744 $order = $fileorder->{$func};
2745
2746 # Account for lines in functions
2747 if (defined($basefile) && defined($filedata->{$basefile})) {
2748 # If the basefile contributes to this function,
2749 # account this function to the basefile.
2750 $graph->{$basefile}->{$func} = $filedata->{$basefile};
2751 } else {
2752 # If the basefile does not contribute to this function,
2753 # account this function to the first file contributing
2754 # lines.
2755 $graph->{$order->[0]}->{$func} =
2756 $filedata->{$order->[0]};
2757 }
2758
2759 foreach $file (keys(%{$filedata})) {
2760 # Account for instrumented lines
2761 $linedata = $filedata->{$file};
2762 push(@{$instr->{$file}}, @$linedata);
2763 }
2764 }
2765 # Clean up array of instrumented lines
2766 foreach $file (keys(%{$instr})) {
2767 $instr->{$file} = [ sort_uniq(@{$instr->{$file}}) ];
2768 }
2769
2770 return ($instr, $graph);
2771 }
2772
2773 #
2774 # graph_add_order(fileorder, function, filename)
2775 #
2776 # Add an entry for filename to the fileorder data set for function.
2777 #
2778
2779 sub graph_add_order($$$)
2780 {
2781 my ($fileorder, $function, $filename) = @_;
2782 my $item;
2783 my $list;
2784
2785 $list = $fileorder->{$function};
2786 foreach $item (@$list) {
2787 if ($item eq $filename) {
2788 return;
2789 }
2790 }
2791 push(@$list, $filename);
2792 $fileorder->{$function} = $list;
2793 }
2794
2795 #
2796 # read_bb_word(handle[, description])
2797 #
2798 # Read and return a word in .bb format from handle.
2799 #
2800
2801 sub read_bb_word(*;$)
2802 {
2803 my ($handle, $desc) = @_;
2804
2805 return graph_read($handle, 4, $desc);
2806 }
2807
2808 #
2809 # read_bb_value(handle[, description])
2810 #
2811 # Read a word in .bb format from handle and return the word and its integer
2812 # value.
2813 #
2814
2815 sub read_bb_value(*;$)
2816 {
2817 my ($handle, $desc) = @_;
2818 my $word;
2819
2820 $word = read_bb_word($handle, $desc);
2821 return undef if (!defined($word));
2822
2823 return ($word, unpack("V", $word));
2824 }
2825
2826 #
2827 # read_bb_string(handle, delimiter)
2828 #
2829 # Read and return a string in .bb format from handle up to the specified
2830 # delimiter value.
2831 #
2832
2833 sub read_bb_string(*$)
2834 {
2835 my ($handle, $delimiter) = @_;
2836 my $word;
2837 my $value;
2838 my $string = "";
2839
2840 graph_expect("string");
2841 do {
2842 ($word, $value) = read_bb_value($handle, "string or delimiter");
2843 return undef if (!defined($value));
2844 if ($value != $delimiter) {
2845 $string .= $word;
2846 }
2847 } while ($value != $delimiter);
2848 $string =~ s/\0//g;
2849
2850 return $string;
2851 }
2852
2853 #
2854 # read_bb(filename)
2855 #
2856 # Read the contents of the specified .bb file and return (instr, graph), where:
2857 #
2858 # instr : filename -> line data
2859 # line data : [ line1, line2, ... ]
2860 #
2861 # graph : filename -> file_data
2862 # file_data : function name -> line_data
2863 # line_data : [ line1, line2, ... ]
2864 #
2865 # See the gcov info pages of gcc 2.95 for a description of the .bb file format.
2866 #
2867
2868 sub read_bb($)
2869 {
2870 my ($bb_filename) = @_;
2871 my $minus_one = 0x80000001;
2872 my $minus_two = 0x80000002;
2873 my $value;
2874 my $filename;
2875 my $function;
2876 my $bb = {};
2877 my $fileorder = {};
2878 my $instr;
2879 my $graph;
2880 local *HANDLE;
2881
2882 open(HANDLE, "<", $bb_filename) or goto open_error;
2883 binmode(HANDLE);
2884 while (!eof(HANDLE)) {
2885 $value = read_bb_value(*HANDLE, "data word");
2886 goto incomplete if (!defined($value));
2887 if ($value == $minus_one) {
2888 # Source file name
2889 graph_expect("filename");
2890 $filename = read_bb_string(*HANDLE, $minus_one);
2891 goto incomplete if (!defined($filename));
2892 } elsif ($value == $minus_two) {
2893 # Function name
2894 graph_expect("function name");
2895 $function = read_bb_string(*HANDLE, $minus_two);
2896 goto incomplete if (!defined($function));
2897 } elsif ($value > 0) {
2898 # Line number
2899 if (!defined($filename) || !defined($function)) {
2900 warn("WARNING: unassigned line number ".
2901 "$value\n");
2902 next;
2903 }
2904 push(@{$bb->{$function}->{$filename}}, $value);
2905 graph_add_order($fileorder, $function, $filename);
2906 }
2907 }
2908 close(HANDLE);
2909 ($instr, $graph) = graph_from_bb($bb, $fileorder, $bb_filename);
2910 graph_cleanup($graph);
2911
2912 return ($instr, $graph);
2913
2914 open_error:
2915 graph_error($bb_filename, "could not open file");
2916 return undef;
2917 incomplete:
2918 graph_error($bb_filename, "reached unexpected end of file");
2919 return undef;
2920 }
2921
2922 #
2923 # read_bbg_word(handle[, description])
2924 #
2925 # Read and return a word in .bbg format.
2926 #
2927
2928 sub read_bbg_word(*;$)
2929 {
2930 my ($handle, $desc) = @_;
2931
2932 return graph_read($handle, 4, $desc);
2933 }
2934
2935 #
2936 # read_bbg_value(handle[, description])
2937 #
2938 # Read a word in .bbg format from handle and return its integer value.
2939 #
2940
2941 sub read_bbg_value(*;$)
2942 {
2943 my ($handle, $desc) = @_;
2944 my $word;
2945
2946 $word = read_bbg_word($handle, $desc);
2947 return undef if (!defined($word));
2948
2949 return unpack("N", $word);
2950 }
2951
2952 #
2953 # read_bbg_string(handle)
2954 #
2955 # Read and return a string in .bbg format.
2956 #
2957
2958 sub read_bbg_string(*)
2959 {
2960 my ($handle, $desc) = @_;
2961 my $length;
2962 my $string;
2963
2964 graph_expect("string");
2965 # Read string length
2966 $length = read_bbg_value($handle, "string length");
2967 return undef if (!defined($length));
2968 if ($length == 0) {
2969 return "";
2970 }
2971 # Read string
2972 $string = graph_read($handle, $length, "string");
2973 return undef if (!defined($string));
2974 # Skip padding
2975 graph_skip($handle, 4 - $length % 4, "string padding") or return undef;
2976
2977 return $string;
2978 }
2979
2980 #
2981 # read_bbg_lines_record(handle, bbg_filename, bb, fileorder, filename,
2982 # function)
2983 #
2984 # Read a bbg format lines record from handle and add the relevant data to
2985 # bb and fileorder. Return filename on success, undef on error.
2986 #
2987
2988 sub read_bbg_lines_record(*$$$$$)
2989 {
2990 my ($handle, $bbg_filename, $bb, $fileorder, $filename, $function) = @_;
2991 my $string;
2992 my $lineno;
2993
2994 graph_expect("lines record");
2995 # Skip basic block index
2996 graph_skip($handle, 4, "basic block index") or return undef;
2997 while (1) {
2998 # Read line number
2999 $lineno = read_bbg_value($handle, "line number");
3000 return undef if (!defined($lineno));
3001 if ($lineno == 0) {
3002 # Got a marker for a new filename
3003 graph_expect("filename");
3004 $string = read_bbg_string($handle);
3005 return undef if (!defined($string));
3006 # Check for end of record
3007 if ($string eq "") {
3008 return $filename;
3009 }
3010 $filename = $string;
3011 if (!exists($bb->{$function}->{$filename})) {
3012 $bb->{$function}->{$filename} = [];
3013 }
3014 next;
3015 }
3016 # Got an actual line number
3017 if (!defined($filename)) {
3018 warn("WARNING: unassigned line number in ".
3019 "$bbg_filename\n");
3020 next;
3021 }
3022 push(@{$bb->{$function}->{$filename}}, $lineno);
3023 graph_add_order($fileorder, $function, $filename);
3024 }
3025 }
3026
3027 #
3028 # read_bbg(filename)
3029 #
3030 # Read the contents of the specified .bbg file and return the following mapping:
3031 # graph: filename -> file_data
3032 # file_data: function name -> line_data
3033 # line_data: [ line1, line2, ... ]
3034 #
3035 # See the gcov-io.h file in the SLES 9 gcc 3.3.3 source code for a description
3036 # of the .bbg format.
3037 #
3038
3039 sub read_bbg($)
3040 {
3041 my ($bbg_filename) = @_;
3042 my $file_magic = 0x67626267;
3043 my $tag_function = 0x01000000;
3044 my $tag_lines = 0x01450000;
3045 my $word;
3046 my $tag;
3047 my $length;
3048 my $function;
3049 my $filename;
3050 my $bb = {};
3051 my $fileorder = {};
3052 my $instr;
3053 my $graph;
3054 local *HANDLE;
3055
3056 open(HANDLE, "<", $bbg_filename) or goto open_error;
3057 binmode(HANDLE);
3058 # Read magic
3059 $word = read_bbg_value(*HANDLE, "file magic");
3060 goto incomplete if (!defined($word));
3061 # Check magic
3062 if ($word != $file_magic) {
3063 goto magic_error;
3064 }
3065 # Skip version
3066 graph_skip(*HANDLE, 4, "version") or goto incomplete;
3067 while (!eof(HANDLE)) {
3068 # Read record tag
3069 $tag = read_bbg_value(*HANDLE, "record tag");
3070 goto incomplete if (!defined($tag));
3071 # Read record length
3072 $length = read_bbg_value(*HANDLE, "record length");
3073 goto incomplete if (!defined($tag));
3074 if ($tag == $tag_function) {
3075 graph_expect("function record");
3076 # Read function name
3077 graph_expect("function name");
3078 $function = read_bbg_string(*HANDLE);
3079 goto incomplete if (!defined($function));
3080 $filename = undef;
3081 # Skip function checksum
3082 graph_skip(*HANDLE, 4, "function checksum")
3083 or goto incomplete;
3084 } elsif ($tag == $tag_lines) {
3085 # Read lines record
3086 $filename = read_bbg_lines_record(HANDLE, $bbg_filename,
3087 $bb, $fileorder, $filename,
3088 $function);
3089 goto incomplete if (!defined($filename));
3090 } else {
3091 # Skip record contents
3092 graph_skip(*HANDLE, $length, "unhandled record")
3093 or goto incomplete;
3094 }
3095 }
3096 close(HANDLE);
3097 ($instr, $graph) = graph_from_bb($bb, $fileorder, $bbg_filename);
3098 graph_cleanup($graph);
3099
3100 return ($instr, $graph);
3101
3102 open_error:
3103 graph_error($bbg_filename, "could not open file");
3104 return undef;
3105 incomplete:
3106 graph_error($bbg_filename, "reached unexpected end of file");
3107 return undef;
3108 magic_error:
3109 graph_error($bbg_filename, "found unrecognized bbg file magic");
3110 return undef;
3111 }
3112
3113 #
3114 # read_gcno_word(handle[, description, peek])
3115 #
3116 # Read and return a word in .gcno format.
3117 #
3118
3119 sub read_gcno_word(*;$$)
3120 {
3121 my ($handle, $desc, $peek) = @_;
3122
3123 return graph_read($handle, 4, $desc, $peek);
3124 }
3125
3126 #
3127 # read_gcno_value(handle, big_endian[, description, peek])
3128 #
3129 # Read a word in .gcno format from handle and return its integer value
3130 # according to the specified endianness. If PEEK is non-zero, reset file
3131 # position after read.
3132 #
3133
3134 sub read_gcno_value(*$;$$)
3135 {
3136 my ($handle, $big_endian, $desc, $peek) = @_;
3137 my $word;
3138 my $pos;
3139
3140 $word = read_gcno_word($handle, $desc, $peek);
3141 return undef if (!defined($word));
3142 if ($big_endian) {
3143 return unpack("N", $word);
3144 } else {
3145 return unpack("V", $word);
3146 }
3147 }
3148
3149 #
3150 # read_gcno_string(handle, big_endian)
3151 #
3152 # Read and return a string in .gcno format.
3153 #
3154
3155 sub read_gcno_string(*$)
3156 {
3157 my ($handle, $big_endian) = @_;
3158 my $length;
3159 my $string;
3160
3161 graph_expect("string");
3162 # Read string length
3163 $length = read_gcno_value($handle, $big_endian, "string length");
3164 return undef if (!defined($length));
3165 if ($length == 0) {
3166 return "";
3167 }
3168 $length *= 4;
3169 # Read string
3170 $string = graph_read($handle, $length, "string and padding");
3171 return undef if (!defined($string));
3172 $string =~ s/\0//g;
3173
3174 return $string;
3175 }
3176
3177 #
3178 # read_gcno_lines_record(handle, gcno_filename, bb, fileorder, filename,
3179 # function, big_endian)
3180 #
3181 # Read a gcno format lines record from handle and add the relevant data to
3182 # bb and fileorder. Return filename on success, undef on error.
3183 #
3184
3185 sub read_gcno_lines_record(*$$$$$$)
3186 {
3187 my ($handle, $gcno_filename, $bb, $fileorder, $filename, $function,
3188 $big_endian) = @_;
3189 my $string;
3190 my $lineno;
3191
3192 graph_expect("lines record");
3193 # Skip basic block index
3194 graph_skip($handle, 4, "basic block index") or return undef;
3195 while (1) {
3196 # Read line number
3197 $lineno = read_gcno_value($handle, $big_endian, "line number");
3198 return undef if (!defined($lineno));
3199 if ($lineno == 0) {
3200 # Got a marker for a new filename
3201 graph_expect("filename");
3202 $string = read_gcno_string($handle, $big_endian);
3203 return undef if (!defined($string));
3204 # Check for end of record
3205 if ($string eq "") {
3206 return $filename;
3207 }
3208 $filename = $string;
3209 if (!exists($bb->{$function}->{$filename})) {
3210 $bb->{$function}->{$filename} = [];
3211 }
3212 next;
3213 }
3214 # Got an actual line number
3215 if (!defined($filename)) {
3216 warn("WARNING: unassigned line number in ".
3217 "$gcno_filename\n");
3218 next;
3219 }
3220 # Add to list
3221 push(@{$bb->{$function}->{$filename}}, $lineno);
3222 graph_add_order($fileorder, $function, $filename);
3223 }
3224 }
3225
3226 #
3227 # determine_gcno_split_crc(handle, big_endian, rec_length)
3228 #
3229 # Determine if HANDLE refers to a .gcno file with a split checksum function
3230 # record format. Return non-zero in case of split checksum format, zero
3231 # otherwise, undef in case of read error.
3232 #
3233
3234 sub determine_gcno_split_crc($$$)
3235 {
3236 my ($handle, $big_endian, $rec_length) = @_;
3237 my $strlen;
3238 my $overlong_string;
3239
3240 return 1 if ($gcov_version >= $GCOV_VERSION_4_7_0);
3241 return 1 if (is_compat($COMPAT_MODE_SPLIT_CRC));
3242
3243 # Heuristic:
3244 # Decide format based on contents of next word in record:
3245 # - pre-gcc 4.7
3246 # This is the function name length / 4 which should be
3247 # less than the remaining record length
3248 # - gcc 4.7
3249 # This is a checksum, likely with high-order bits set,
3250 # resulting in a large number
3251 $strlen = read_gcno_value($handle, $big_endian, undef, 1);
3252 return undef if (!defined($strlen));
3253 $overlong_string = 1 if ($strlen * 4 >= $rec_length - 12);
3254
3255 if ($overlong_string) {
3256 if (is_compat_auto($COMPAT_MODE_SPLIT_CRC)) {
3257 info("Auto-detected compatibility mode for split ".
3258 "checksum .gcno file format\n");
3259
3260 return 1;
3261 } else {
3262 # Sanity check
3263 warn("Found overlong string in function record: ".
3264 "try '--compat split_crc'\n");
3265 }
3266 }
3267
3268 return 0;
3269 }
3270
3271 #
3272 # read_gcno_function_record(handle, graph, big_endian, rec_length)
3273 #
3274 # Read a gcno format function record from handle and add the relevant data
3275 # to graph. Return (filename, function) on success, undef on error.
3276 #
3277
3278 sub read_gcno_function_record(*$$$$)
3279 {
3280 my ($handle, $bb, $fileorder, $big_endian, $rec_length) = @_;
3281 my $filename;
3282 my $function;
3283 my $lineno;
3284 my $lines;
3285
3286 graph_expect("function record");
3287 # Skip ident and checksum
3288 graph_skip($handle, 8, "function ident and checksum") or return undef;
3289 # Determine if this is a function record with split checksums
3290 if (!defined($gcno_split_crc)) {
3291 $gcno_split_crc = determine_gcno_split_crc($handle, $big_endian,
3292 $rec_length);
3293 return undef if (!defined($gcno_split_crc));
3294 }
3295 # Skip cfg checksum word in case of split checksums
3296 graph_skip($handle, 4, "function cfg checksum") if ($gcno_split_crc);
3297 # Read function name
3298 graph_expect("function name");
3299 $function = read_gcno_string($handle, $big_endian);
3300 return undef if (!defined($function));
3301 # Read filename
3302 graph_expect("filename");
3303 $filename = read_gcno_string($handle, $big_endian);
3304 return undef if (!defined($filename));
3305 # Read first line number
3306 $lineno = read_gcno_value($handle, $big_endian, "initial line number");
3307 return undef if (!defined($lineno));
3308 # Add to list
3309 push(@{$bb->{$function}->{$filename}}, $lineno);
3310 graph_add_order($fileorder, $function, $filename);
3311
3312 return ($filename, $function);
3313 }
3314
3315 #
3316 # read_gcno(filename)
3317 #
3318 # Read the contents of the specified .gcno file and return the following
3319 # mapping:
3320 # graph: filename -> file_data
3321 # file_data: function name -> line_data
3322 # line_data: [ line1, line2, ... ]
3323 #
3324 # See the gcov-io.h file in the gcc 3.3 source code for a description of
3325 # the .gcno format.
3326 #
3327
3328 sub read_gcno($)
3329 {
3330 my ($gcno_filename) = @_;
3331 my $file_magic = 0x67636e6f;
3332 my $tag_function = 0x01000000;
3333 my $tag_lines = 0x01450000;
3334 my $big_endian;
3335 my $word;
3336 my $tag;
3337 my $length;
3338 my $filename;
3339 my $function;
3340 my $bb = {};
3341 my $fileorder = {};
3342 my $instr;
3343 my $graph;
3344 local *HANDLE;
3345
3346 open(HANDLE, "<", $gcno_filename) or goto open_error;
3347 binmode(HANDLE);
3348 # Read magic
3349 $word = read_gcno_word(*HANDLE, "file magic");
3350 goto incomplete if (!defined($word));
3351 # Determine file endianness
3352 if (unpack("N", $word) == $file_magic) {
3353 $big_endian = 1;
3354 } elsif (unpack("V", $word) == $file_magic) {
3355 $big_endian = 0;
3356 } else {
3357 goto magic_error;
3358 }
3359 # Skip version and stamp
3360 graph_skip(*HANDLE, 8, "version and stamp") or goto incomplete;
3361 while (!eof(HANDLE)) {
3362 my $next_pos;
3363 my $curr_pos;
3364
3365 # Read record tag
3366 $tag = read_gcno_value(*HANDLE, $big_endian, "record tag");
3367 goto incomplete if (!defined($tag));
3368 # Read record length
3369 $length = read_gcno_value(*HANDLE, $big_endian,
3370 "record length");
3371 goto incomplete if (!defined($length));
3372 # Convert length to bytes
3373 $length *= 4;
3374 # Calculate start of next record
3375 $next_pos = tell(HANDLE);
3376 goto tell_error if ($next_pos == -1);
3377 $next_pos += $length;
3378 # Process record
3379 if ($tag == $tag_function) {
3380 ($filename, $function) = read_gcno_function_record(
3381 *HANDLE, $bb, $fileorder, $big_endian,
3382 $length);
3383 goto incomplete if (!defined($function));
3384 } elsif ($tag == $tag_lines) {
3385 # Read lines record
3386 $filename = read_gcno_lines_record(*HANDLE,
3387 $gcno_filename, $bb, $fileorder,
3388 $filename, $function,
3389 $big_endian);
3390 goto incomplete if (!defined($filename));
3391 } else {
3392 # Skip record contents
3393 graph_skip(*HANDLE, $length, "unhandled record")
3394 or goto incomplete;
3395 }
3396 # Ensure that we are at the start of the next record
3397 $curr_pos = tell(HANDLE);
3398 goto tell_error if ($curr_pos == -1);
3399 next if ($curr_pos == $next_pos);
3400 goto record_error if ($curr_pos > $next_pos);
3401 graph_skip(*HANDLE, $next_pos - $curr_pos,
3402 "unhandled record content")
3403 or goto incomplete;
3404 }
3405 close(HANDLE);
3406 ($instr, $graph) = graph_from_bb($bb, $fileorder, $gcno_filename);
3407 graph_cleanup($graph);
3408
3409 return ($instr, $graph);
3410
3411 open_error:
3412 graph_error($gcno_filename, "could not open file");
3413 return undef;
3414 incomplete:
3415 graph_error($gcno_filename, "reached unexpected end of file");
3416 return undef;
3417 magic_error:
3418 graph_error($gcno_filename, "found unrecognized gcno file magic");
3419 return undef;
3420 tell_error:
3421 graph_error($gcno_filename, "could not determine file position");
3422 return undef;
3423 record_error:
3424 graph_error($gcno_filename, "found unrecognized record format");
3425 return undef;
3426 }
3427
3428 sub debug($)
3429 {
3430 my ($msg) = @_;
3431
3432 return if (!$debug);
3433 print(STDERR "DEBUG: $msg");
3434 }
3435
3436 #
3437 # get_gcov_capabilities
3438 #
3439 # Determine the list of available gcov options.
3440 #
3441
3442 sub get_gcov_capabilities()
3443 {
3444 my $help = `$gcov_tool --help`;
3445 my %capabilities;
3446
3447 foreach (split(/\n/, $help)) {
3448 next if (!/--(\S+)/);
3449 next if ($1 eq 'help');
3450 next if ($1 eq 'version');
3451 next if ($1 eq 'object-directory');
3452
3453 $capabilities{$1} = 1;
3454 debug("gcov has capability '$1'\n");
3455 }
3456
3457 return \%capabilities;
3458 }
3459
3460 #
3461 # parse_ignore_errors(@ignore_errors)
3462 #
3463 # Parse user input about which errors to ignore.
3464 #
3465
3466 sub parse_ignore_errors(@)
3467 {
3468 my (@ignore_errors) = @_;
3469 my @items;
3470 my $item;
3471
3472 return if (!@ignore_errors);
3473
3474 foreach $item (@ignore_errors) {
3475 $item =~ s/\s//g;
3476 if ($item =~ /,/) {
3477 # Split and add comma-separated parameters
3478 push(@items, split(/,/, $item));
3479 } else {
3480 # Add single parameter
3481 push(@items, $item);
3482 }
3483 }
3484 foreach $item (@items) {
3485 my $item_id = $ERROR_ID{lc($item)};
3486
3487 if (!defined($item_id)) {
3488 die("ERROR: unknown argument for --ignore-errors: ".
3489 "$item\n");
3490 }
3491 $ignore[$item_id] = 1;
3492 }
3493 }
3494
3495 #
3496 # is_external(filename)
3497 #
3498 # Determine if a file is located outside of the specified data directories.
3499 #
3500
3501 sub is_external($)
3502 {
3503 my ($filename) = @_;
3504 my $dir;
3505
3506 foreach $dir (@internal_dirs) {
3507 return 0 if ($filename =~ /^\Q$dir\/\E/);
3508 }
3509 return 1;
3510 }
3511
3512 #
3513 # compat_name(mode)
3514 #
3515 # Return the name of compatibility mode MODE.
3516 #
3517
3518 sub compat_name($)
3519 {
3520 my ($mode) = @_;
3521 my $name = $COMPAT_MODE_TO_NAME{$mode};
3522
3523 return $name if (defined($name));
3524
3525 return "<unknown>";
3526 }
3527
3528 #
3529 # parse_compat_modes(opt)
3530 #
3531 # Determine compatibility mode settings.
3532 #
3533
3534 sub parse_compat_modes($)
3535 {
3536 my ($opt) = @_;
3537 my @opt_list;
3538 my %specified;
3539
3540 # Initialize with defaults
3541 %compat_value = %COMPAT_MODE_DEFAULTS;
3542
3543 # Add old style specifications
3544 if (defined($opt_compat_libtool)) {
3545 $compat_value{$COMPAT_MODE_LIBTOOL} =
3546 $opt_compat_libtool ? $COMPAT_VALUE_ON
3547 : $COMPAT_VALUE_OFF;
3548 }
3549
3550 # Parse settings
3551 if (defined($opt)) {
3552 @opt_list = split(/\s*,\s*/, $opt);
3553 }
3554 foreach my $directive (@opt_list) {
3555 my ($mode, $value);
3556
3557 # Either
3558 # mode=off|on|auto or
3559 # mode (implies on)
3560 if ($directive !~ /^(\w+)=(\w+)$/ &&
3561 $directive !~ /^(\w+)$/) {
3562 die("ERROR: Unknown compatibility mode specification: ".
3563 "$directive!\n");
3564 }
3565 # Determine mode
3566 $mode = $COMPAT_NAME_TO_MODE{lc($1)};
3567 if (!defined($mode)) {
3568 die("ERROR: Unknown compatibility mode '$1'!\n");
3569 }
3570 $specified{$mode} = 1;
3571 # Determine value
3572 if (defined($2)) {
3573 $value = $COMPAT_NAME_TO_VALUE{lc($2)};
3574 if (!defined($value)) {
3575 die("ERROR: Unknown compatibility mode ".
3576 "value '$2'!\n");
3577 }
3578 } else {
3579 $value = $COMPAT_VALUE_ON;
3580 }
3581 $compat_value{$mode} = $value;
3582 }
3583 # Perform auto-detection
3584 foreach my $mode (sort(keys(%compat_value))) {
3585 my $value = $compat_value{$mode};
3586 my $is_autodetect = "";
3587 my $name = compat_name($mode);
3588
3589 if ($value == $COMPAT_VALUE_AUTO) {
3590 my $autodetect = $COMPAT_MODE_AUTO{$mode};
3591
3592 if (!defined($autodetect)) {
3593 die("ERROR: No auto-detection for ".
3594 "mode '$name' available!\n");
3595 }
3596
3597 if (ref($autodetect) eq "CODE") {
3598 $value = &$autodetect();
3599 $compat_value{$mode} = $value;
3600 $is_autodetect = " (auto-detected)";
3601 }
3602 }
3603
3604 if ($specified{$mode}) {
3605 if ($value == $COMPAT_VALUE_ON) {
3606 info("Enabling compatibility mode ".
3607 "'$name'$is_autodetect\n");
3608 } elsif ($value == $COMPAT_VALUE_OFF) {
3609 info("Disabling compatibility mode ".
3610 "'$name'$is_autodetect\n");
3611 } else {
3612 info("Using delayed auto-detection for ".
3613 "compatibility mode ".
3614 "'$name'\n");
3615 }
3616 }
3617 }
3618 }
3619
3620 sub compat_hammer_autodetect()
3621 {
3622 if ($gcov_version_string =~ /suse/i && $gcov_version == 0x30303 ||
3623 $gcov_version_string =~ /mandrake/i && $gcov_version == 0x30302)
3624 {
3625 info("Auto-detected compatibility mode for GCC 3.3 (hammer)\n");
3626 return $COMPAT_VALUE_ON;
3627 }
3628 return $COMPAT_VALUE_OFF;
3629 }
3630
3631 #
3632 # is_compat(mode)
3633 #
3634 # Return non-zero if compatibility mode MODE is enabled.
3635 #
3636
3637 sub is_compat($)
3638 {
3639 my ($mode) = @_;
3640
3641 return 1 if ($compat_value{$mode} == $COMPAT_VALUE_ON);
3642 return 0;
3643 }
3644
3645 #
3646 # is_compat_auto(mode)
3647 #
3648 # Return non-zero if compatibility mode MODE is set to auto-detect.
3649 #
3650
3651 sub is_compat_auto($)
3652 {
3653 my ($mode) = @_;
3654
3655 return 1 if ($compat_value{$mode} == $COMPAT_VALUE_AUTO);
3656 return 0;
3657 }
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