Home | History | Annotate | Download | only in bin
      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.10';
     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 genpng_warn_handler($);
     49 sub genpng_die_handler($);
     50 
     51 
     52 #
     53 # Code entry point
     54 #
     55 
     56 # Prettify version string
     57 $lcov_version =~ s/\$\s*Revision\s*:?\s*(\S+)\s*\$/$1/;
     58 
     59 # Check whether required module GD.pm is installed
     60 if (check_and_load_module("GD"))
     61 {
     62 	# Note: cannot use die() to print this message because inserting this
     63 	# code into another script via do() would not fail as required!
     64 	print(STDERR <<END_OF_TEXT)
     65 ERROR: required module GD.pm not found on this system (see www.cpan.org).
     66 END_OF_TEXT
     67 	;
     68 	exit(2);
     69 }
     70 
     71 # Check whether we're called from the command line or from another script
     72 if (!caller)
     73 {
     74 	my $filename;
     75 	my $tab_size = 4;
     76 	my $width = 80;
     77 	my $out_filename;
     78 	my $help;
     79 	my $version;
     80 
     81 	$SIG{__WARN__} = \&genpng_warn_handler;
     82 	$SIG{__DIE__} = \&genpng_die_handler;
     83 
     84 	# Parse command line options
     85 	if (!GetOptions("tab-size=i" => \$tab_size,
     86 			"width=i" => \$width,
     87 			"output-filename=s" => \$out_filename,
     88 			"help" => \$help,
     89 			"version" => \$version))
     90 	{
     91 		print(STDERR "Use $tool_name --help to get usage ".
     92 		      "information\n");
     93 		exit(1);
     94 	}
     95 
     96 	$filename = $ARGV[0];
     97 
     98 	# Check for help flag
     99 	if ($help)
    100 	{
    101 		genpng_print_usage(*STDOUT);
    102 		exit(0);
    103 	}
    104 
    105 	# Check for version flag
    106 	if ($version)
    107 	{
    108 		print("$tool_name: $lcov_version\n");
    109 		exit(0);
    110 	}
    111 
    112 	# Check options
    113 	if (!$filename)
    114 	{
    115 		die("No filename specified\n");
    116 	}
    117 
    118 	# Check for output filename
    119 	if (!$out_filename)
    120 	{
    121 		$out_filename = "$filename.png";
    122 	}
    123 
    124 	genpng_process_file($filename, $out_filename, $width, $tab_size);
    125 	exit(0);
    126 }
    127 
    128 
    129 #
    130 # genpng_print_usage(handle)
    131 #
    132 # Write out command line usage information to given filehandle.
    133 #
    134 
    135 sub genpng_print_usage(*)
    136 {
    137 	local *HANDLE = $_[0];
    138 
    139 	print(HANDLE <<END_OF_USAGE)
    140 Usage: $tool_name [OPTIONS] SOURCEFILE
    141 
    142 Create an overview image for a given source code file of either plain text
    143 or .gcov file format.
    144 
    145   -h, --help                        Print this help, then exit
    146   -v, --version                     Print version number, then exit
    147   -t, --tab-size TABSIZE            Use TABSIZE spaces in place of tab
    148   -w, --width WIDTH                 Set width of output image to WIDTH pixel
    149   -o, --output-filename FILENAME    Write image to FILENAME
    150 
    151 For more information see: $lcov_url
    152 END_OF_USAGE
    153 	;
    154 }
    155 
    156 
    157 #
    158 # check_and_load_module(module_name)
    159 #
    160 # Check whether a module by the given name is installed on this system
    161 # and make it known to the interpreter if available. Return undefined if it
    162 # is installed, an error message otherwise.
    163 #
    164 
    165 sub check_and_load_module($)
    166 {
    167 	eval("use $_[0];");
    168 	return $@;
    169 }
    170 
    171 
    172 #
    173 # genpng_process_file(filename, out_filename, width, tab_size)
    174 #
    175 
    176 sub genpng_process_file($$$$)
    177 {
    178 	my $filename		= $_[0];
    179 	my $out_filename	= $_[1];
    180 	my $width		= $_[2];
    181 	my $tab_size		= $_[3];
    182 	local *HANDLE;
    183 	my @source;
    184 
    185 	open(HANDLE, "<", $filename)
    186 		or die("ERROR: cannot open $filename!\n");
    187 
    188 	# Check for .gcov filename extension
    189 	if ($filename =~ /^(.*).gcov$/)
    190 	{
    191 		# Assume gcov text format
    192 		while (<HANDLE>)
    193 		{
    194 			if (/^\t\t(.*)$/)
    195 			{
    196 				# Uninstrumented line
    197 				push(@source, ":$1");
    198 			}
    199 			elsif (/^      ######    (.*)$/)
    200 			{
    201 				# Line with zero execution count
    202 				push(@source, "0:$1");
    203 			}
    204 			elsif (/^( *)(\d*)    (.*)$/)
    205 			{
    206 				# Line with positive execution count
    207 				push(@source, "$2:$3");
    208 			}
    209 		}
    210 	}
    211 	else
    212 	{
    213 		# Plain text file
    214 		while (<HANDLE>) { push(@source, ":$_"); }
    215 	}
    216 	close(HANDLE);
    217 
    218 	gen_png($out_filename, $width, $tab_size, @source);
    219 }
    220 
    221 
    222 #
    223 # gen_png(filename, width, tab_size, source)
    224 #
    225 # Write an overview PNG file to FILENAME. Source code is defined by SOURCE
    226 # which is a list of lines <count>:<source code> per source code line.
    227 # The output image will be made up of one pixel per character of source,
    228 # coloring will be done according to execution counts. WIDTH defines the
    229 # image width. TAB_SIZE specifies the number of spaces to use as replacement
    230 # string for tabulator signs in source code text.
    231 #
    232 # Die on error.
    233 #
    234 
    235 sub gen_png($$$@)
    236 {
    237 	my $filename = shift(@_);	# Filename for PNG file
    238 	my $overview_width = shift(@_);	# Imagewidth for image
    239 	my $tab_size = shift(@_);	# Replacement string for tab signs
    240 	my @source = @_;	# Source code as passed via argument 2
    241 	my $height;		# Height as define by source size
    242 	my $overview;		# Source code overview image data
    243 	my $col_plain_back;	# Color for overview background
    244 	my $col_plain_text;	# Color for uninstrumented text
    245 	my $col_cov_back;	# Color for background of covered lines
    246 	my $col_cov_text;	# Color for text of covered lines
    247 	my $col_nocov_back;	# Color for background of lines which
    248 				# were not covered (count == 0)
    249 	my $col_nocov_text;	# Color for test of lines which were not
    250 				# covered (count == 0)
    251 	my $col_hi_back;	# Color for background of highlighted lines
    252 	my $col_hi_text;	# Color for text of highlighted lines
    253 	my $line;		# Current line during iteration
    254 	my $row = 0;		# Current row number during iteration
    255 	my $column;		# Current column number during iteration
    256 	my $color_text;		# Current text color during iteration
    257 	my $color_back;		# Current background color during iteration
    258 	my $last_count;		# Count of last processed line
    259 	my $count;		# Count of current line
    260 	my $source;		# Source code of current line
    261 	my $replacement;	# Replacement string for tabulator chars
    262 	local *PNG_HANDLE;	# Handle for output PNG file
    263 
    264 	# Handle empty source files
    265 	if (!@source) {
    266 		@source = ( "" );
    267 	}
    268 	$height = scalar(@source);
    269 	# Create image
    270 	$overview = new GD::Image($overview_width, $height)
    271 		or die("ERROR: cannot allocate overview image!\n");
    272 
    273 	# Define colors
    274 	$col_plain_back	= $overview->colorAllocate(0xff, 0xff, 0xff);
    275 	$col_plain_text	= $overview->colorAllocate(0xaa, 0xaa, 0xaa);
    276 	$col_cov_back	= $overview->colorAllocate(0xaa, 0xa7, 0xef);
    277 	$col_cov_text	= $overview->colorAllocate(0x5d, 0x5d, 0xea);
    278 	$col_nocov_back = $overview->colorAllocate(0xff, 0x00, 0x00);
    279 	$col_nocov_text = $overview->colorAllocate(0xaa, 0x00, 0x00);
    280 	$col_hi_back = $overview->colorAllocate(0x00, 0xff, 0x00);
    281 	$col_hi_text = $overview->colorAllocate(0x00, 0xaa, 0x00);
    282 
    283 	# Visualize each line
    284 	foreach $line (@source)
    285 	{
    286 		# Replace tabs with spaces to keep consistent with source
    287 		# code view
    288 		while ($line =~ /^([^\t]*)(\t)/)
    289 		{
    290 			$replacement = " "x($tab_size - ((length($1) - 1) %
    291 				       $tab_size));
    292 			$line =~ s/^([^\t]*)(\t)/$1$replacement/;
    293 		}
    294 
    295 		# Skip lines which do not follow the <count>:<line>
    296 		# specification, otherwise $1 = count, $2 = source code
    297 		if (!($line =~ /(\*?)(\d*):(.*)$/)) { next; }
    298 		$count = $2;
    299 		$source = $3;
    300 
    301 		# Decide which color pair to use
    302 
    303 		# If this line was not instrumented but the one before was,
    304 		# take the color of that line to widen color areas in
    305 		# resulting image
    306 		if (($count eq "") && defined($last_count) &&
    307 		    ($last_count ne ""))
    308 		{
    309 			$count = $last_count;
    310 		}
    311 
    312 		if ($count eq "")
    313 		{
    314 			# Line was not instrumented
    315 			$color_text = $col_plain_text;
    316 			$color_back = $col_plain_back;
    317 		}
    318 		elsif ($count == 0)
    319 		{
    320 			# Line was instrumented but not executed
    321 			$color_text = $col_nocov_text;
    322 			$color_back = $col_nocov_back;
    323 		}
    324 		elsif ($1 eq "*")
    325 		{
    326 			# Line was highlighted
    327 			$color_text = $col_hi_text;
    328 			$color_back = $col_hi_back;
    329 		}
    330 		else
    331 		{
    332 			# Line was instrumented and executed
    333 			$color_text = $col_cov_text;
    334 			$color_back = $col_cov_back;
    335 		}
    336 
    337 		# Write one pixel for each source character
    338 		$column = 0;
    339 		foreach (split("", $source))
    340 		{
    341 			# Check for width
    342 			if ($column >= $overview_width) { last; }
    343 
    344 			if ($_ eq " ")
    345 			{
    346 				# Space
    347 				$overview->setPixel($column++, $row,
    348 						    $color_back);
    349 			}
    350 			else
    351 			{
    352 				# Text
    353 				$overview->setPixel($column++, $row,
    354 						    $color_text);
    355 			}
    356 		}
    357 
    358 		# Fill rest of line		
    359 		while ($column < $overview_width)
    360 		{
    361 			$overview->setPixel($column++, $row, $color_back);
    362 		}
    363 
    364 		$last_count = $2;
    365 
    366 		$row++;
    367 	}
    368 
    369 	# Write PNG file
    370 	open (PNG_HANDLE, ">", $filename)
    371 		or die("ERROR: cannot write png file $filename!\n");
    372 	binmode(*PNG_HANDLE);
    373 	print(PNG_HANDLE $overview->png());
    374 	close(PNG_HANDLE);
    375 }
    376 
    377 sub genpng_warn_handler($)
    378 {
    379 	my ($msg) = @_;
    380 
    381 	warn("$tool_name: $msg");
    382 }
    383 
    384 sub genpng_die_handler($)
    385 {
    386 	my ($msg) = @_;
    387 
    388 	die("$tool_name: $msg");
    389 }
    390