| OLD | NEW | 
|---|
| 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  Loading... | 
| 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  Loading... | 
| 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  Loading... | 
| 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  Loading... | 
| 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  Loading... | 
| 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  Loading... | 
| 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  Loading... | 
| 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  Loading... | 
| 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  Loading... | 
| 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  Loading... | 
| 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 } | 
| OLD | NEW | 
|---|