1 #!/usr/bin/perl -w 2 # 3 # Copyright (c) International Business Machines Corp., 2002 4 # 5 # This program is free software; you can redistribute it and/or modify 6 # it under the terms of the GNU General Public License as published by 7 # the Free Software Foundation; either version 2 of the License, or (at 8 # your option) any later version. 9 # 10 # This program is distributed in the hope that it will be useful, but 11 # WITHOUT ANY WARRANTY; without even the implied warranty of 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 # General Public License for more details. 14 # 15 # You should have received a copy of the GNU General Public License 16 # along with this program; if not, write to the Free Software 17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 # 19 # 20 # genpng 21 # 22 # This script creates an overview PNG image of a source code file by 23 # representing each source code character by a single pixel. 24 # 25 # Note that the PERL module GD.pm is required for this script to work. 26 # It may be obtained from http://www.cpan.org 27 # 28 # History: 29 # 2002-08-26: created by Peter Oberparleiter <Peter.Oberparleiter (at] de.ibm.com> 30 # 31 32 use strict; 33 use File::Basename; 34 use Getopt::Long; 35 36 37 # Constants 38 our $lcov_version = "LCOV version 1.7"; 39 our $lcov_url = "http://ltp.sourceforge.net/coverage/lcov.php"; 40 our $tool_name = basename($0); 41 42 43 # Prototypes 44 sub gen_png($$$@); 45 sub check_and_load_module($); 46 sub genpng_print_usage(*); 47 sub genpng_process_file($$$$); 48 sub warn_handler($); 49 sub die_handler($); 50 51 52 # 53 # Code entry point 54 # 55 56 # Check whether required module GD.pm is installed 57 if (check_and_load_module("GD")) 58 { 59 # Note: cannot use die() to print this message because inserting this 60 # code into another script via do() would not fail as required! 61 print(STDERR <<END_OF_TEXT) 62 ERROR: required module GD.pm not found on this system (see www.cpan.org). 63 END_OF_TEXT 64 ; 65 exit(2); 66 } 67 68 # Check whether we're called from the command line or from another script 69 if (!caller) 70 { 71 my $filename; 72 my $tab_size = 4; 73 my $width = 80; 74 my $out_filename; 75 my $help; 76 my $version; 77 78 $SIG{__WARN__} = \&warn_handler; 79 $SIG{__DIE__} = \&die_handler; 80 81 # Parse command line options 82 if (!GetOptions("tab-size=i" => \$tab_size, 83 "width=i" => \$width, 84 "output-filename=s" => \$out_filename, 85 "help" => \$help, 86 "version" => \$version)) 87 { 88 print(STDERR "Use $tool_name --help to get usage ". 89 "information\n"); 90 exit(1); 91 } 92 93 $filename = $ARGV[0]; 94 95 # Check for help flag 96 if ($help) 97 { 98 genpng_print_usage(*STDOUT); 99 exit(0); 100 } 101 102 # Check for version flag 103 if ($version) 104 { 105 print("$tool_name: $lcov_version\n"); 106 exit(0); 107 } 108 109 # Check options 110 if (!$filename) 111 { 112 die("No filename specified\n"); 113 } 114 115 # Check for output filename 116 if (!$out_filename) 117 { 118 $out_filename = "$filename.png"; 119 } 120 121 genpng_process_file($filename, $out_filename, $width, $tab_size); 122 exit(0); 123 } 124 125 126 # 127 # genpng_print_usage(handle) 128 # 129 # Write out command line usage information to given filehandle. 130 # 131 132 sub genpng_print_usage(*) 133 { 134 local *HANDLE = $_[0]; 135 136 print(HANDLE <<END_OF_USAGE) 137 Usage: $tool_name [OPTIONS] SOURCEFILE 138 139 Create an overview image for a given source code file of either plain text 140 or .gcov file format. 141 142 -h, --help Print this help, then exit 143 -v, --version Print version number, then exit 144 -t, --tab-size TABSIZE Use TABSIZE spaces in place of tab 145 -w, --width WIDTH Set width of output image to WIDTH pixel 146 -o, --output-filename FILENAME Write image to FILENAME 147 148 For more information see: $lcov_url 149 END_OF_USAGE 150 ; 151 } 152 153 154 # 155 # check_and_load_module(module_name) 156 # 157 # Check whether a module by the given name is installed on this system 158 # and make it known to the interpreter if available. Return undefined if it 159 # is installed, an error message otherwise. 160 # 161 162 sub check_and_load_module($) 163 { 164 eval("use $_[0];"); 165 return $@; 166 } 167 168 169 # 170 # genpng_process_file(filename, out_filename, width, tab_size) 171 # 172 173 sub genpng_process_file($$$$) 174 { 175 my $filename = $_[0]; 176 my $out_filename = $_[1]; 177 my $width = $_[2]; 178 my $tab_size = $_[3]; 179 local *HANDLE; 180 my @source; 181 182 open(HANDLE, "<$filename") 183 or die("ERROR: cannot open $filename!\n"); 184 185 # Check for .gcov filename extension 186 if ($filename =~ /^(.*).gcov$/) 187 { 188 # Assume gcov text format 189 while (<HANDLE>) 190 { 191 if (/^\t\t(.*)$/) 192 { 193 # Uninstrumented line 194 push(@source, ":$1"); 195 } 196 elsif (/^ ###### (.*)$/) 197 { 198 # Line with zero execution count 199 push(@source, "0:$1"); 200 } 201 elsif (/^( *)(\d*) (.*)$/) 202 { 203 # Line with positive execution count 204 push(@source, "$2:$3"); 205 } 206 } 207 } 208 else 209 { 210 # Plain text file 211 while (<HANDLE>) { push(@source, ":$_"); } 212 } 213 close(HANDLE); 214 215 gen_png($out_filename, $width, $tab_size, @source); 216 } 217 218 219 # 220 # gen_png(filename, width, tab_size, source) 221 # 222 # Write an overview PNG file to FILENAME. Source code is defined by SOURCE 223 # which is a list of lines <count>:<source code> per source code line. 224 # The output image will be made up of one pixel per character of source, 225 # coloring will be done according to execution counts. WIDTH defines the 226 # image width. TAB_SIZE specifies the number of spaces to use as replacement 227 # string for tabulator signs in source code text. 228 # 229 # Die on error. 230 # 231 232 sub gen_png($$$@) 233 { 234 my $filename = shift(@_); # Filename for PNG file 235 my $overview_width = shift(@_); # Imagewidth for image 236 my $tab_size = shift(@_); # Replacement string for tab signs 237 my @source = @_; # Source code as passed via argument 2 238 my $height = scalar(@source); # Height as define by source size 239 my $overview; # Source code overview image data 240 my $col_plain_back; # Color for overview background 241 my $col_plain_text; # Color for uninstrumented text 242 my $col_cov_back; # Color for background of covered lines 243 my $col_cov_text; # Color for text of covered lines 244 my $col_nocov_back; # Color for background of lines which 245 # were not covered (count == 0) 246 my $col_nocov_text; # Color for test of lines which were not 247 # covered (count == 0) 248 my $col_hi_back; # Color for background of highlighted lines 249 my $col_hi_text; # Color for text of highlighted lines 250 my $line; # Current line during iteration 251 my $row = 0; # Current row number during iteration 252 my $column; # Current column number during iteration 253 my $color_text; # Current text color during iteration 254 my $color_back; # Current background color during iteration 255 my $last_count; # Count of last processed line 256 my $count; # Count of current line 257 my $source; # Source code of current line 258 my $replacement; # Replacement string for tabulator chars 259 local *PNG_HANDLE; # Handle for output PNG file 260 261 # Create image 262 $overview = new GD::Image($overview_width, $height) 263 or die("ERROR: cannot allocate overview image!\n"); 264 265 # Define colors 266 $col_plain_back = $overview->colorAllocate(0xff, 0xff, 0xff); 267 $col_plain_text = $overview->colorAllocate(0xaa, 0xaa, 0xaa); 268 $col_cov_back = $overview->colorAllocate(0xaa, 0xa7, 0xef); 269 $col_cov_text = $overview->colorAllocate(0x5d, 0x5d, 0xea); 270 $col_nocov_back = $overview->colorAllocate(0xff, 0x00, 0x00); 271 $col_nocov_text = $overview->colorAllocate(0xaa, 0x00, 0x00); 272 $col_hi_back = $overview->colorAllocate(0x00, 0xff, 0x00); 273 $col_hi_text = $overview->colorAllocate(0x00, 0xaa, 0x00); 274 275 # Visualize each line 276 foreach $line (@source) 277 { 278 # Replace tabs with spaces to keep consistent with source 279 # code view 280 while ($line =~ /^([^\t]*)(\t)/) 281 { 282 $replacement = " "x($tab_size - ((length($1) - 1) % 283 $tab_size)); 284 $line =~ s/^([^\t]*)(\t)/$1$replacement/; 285 } 286 287 # Skip lines which do not follow the <count>:<line> 288 # specification, otherwise $1 = count, $2 = source code 289 if (!($line =~ /(\*?)(\d*):(.*)$/)) { next; } 290 $count = $2; 291 $source = $3; 292 293 # Decide which color pair to use 294 295 # If this line was not instrumented but the one before was, 296 # take the color of that line to widen color areas in 297 # resulting image 298 if (($count eq "") && defined($last_count) && 299 ($last_count ne "")) 300 { 301 $count = $last_count; 302 } 303 304 if ($count eq "") 305 { 306 # Line was not instrumented 307 $color_text = $col_plain_text; 308 $color_back = $col_plain_back; 309 } 310 elsif ($count == 0) 311 { 312 # Line was instrumented but not executed 313 $color_text = $col_nocov_text; 314 $color_back = $col_nocov_back; 315 } 316 elsif ($1 eq "*") 317 { 318 # Line was highlighted 319 $color_text = $col_hi_text; 320 $color_back = $col_hi_back; 321 } 322 else 323 { 324 # Line was instrumented and executed 325 $color_text = $col_cov_text; 326 $color_back = $col_cov_back; 327 } 328 329 # Write one pixel for each source character 330 $column = 0; 331 foreach (split("", $source)) 332 { 333 # Check for width 334 if ($column >= $overview_width) { last; } 335 336 if ($_ eq " ") 337 { 338 # Space 339 $overview->setPixel($column++, $row, 340 $color_back); 341 } 342 else 343 { 344 # Text 345 $overview->setPixel($column++, $row, 346 $color_text); 347 } 348 } 349 350 # Fill rest of line 351 while ($column < $overview_width) 352 { 353 $overview->setPixel($column++, $row, $color_back); 354 } 355 356 $last_count = $2; 357 358 $row++; 359 } 360 361 # Write PNG file 362 open (PNG_HANDLE, ">$filename") 363 or die("ERROR: cannot write png file $filename!\n"); 364 binmode(*PNG_HANDLE); 365 print(PNG_HANDLE $overview->png()); 366 close(PNG_HANDLE); 367 } 368 369 sub warn_handler($) 370 { 371 my ($msg) = @_; 372 373 warn("$tool_name: $msg"); 374 } 375 376 sub die_handler($) 377 { 378 my ($msg) = @_; 379 380 die("$tool_name: $msg"); 381 } 382