summaryrefslogtreecommitdiffstats
blob: b4d90c2d1da2345251d8f0835b2b0dec71d260aa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
#!/usr/bin/perl -w
#
#   Copyright (c) International Business Machines  Corp., 2002
#
#   This program is free software;  you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or (at
#   your option) any later version.
#
#   This program is distributed in the hope that it will be useful, but
#   WITHOUT ANY WARRANTY;  without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#   General Public License for more details.                 
#
#   You should have received a copy of the GNU General Public License
#   along with this program;  if not, write to the Free Software
#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
#
# genpng
#
#   This script creates an overview PNG image of a source code file by
#   representing each source code character by a single pixel.
#
#   Note that the PERL module GD.pm is required for this script to work.
#   It may be obtained from http://www.cpan.org
#
# History:
#   2002-08-26: created by Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com>
#

use strict;
use File::Basename; 
use Getopt::Long;


# Constants
our $lcov_version	= "LCOV version 1.7";
our $lcov_url		= "http://ltp.sourceforge.net/coverage/lcov.php";
our $tool_name		= basename($0);


# Prototypes
sub gen_png($$$@);
sub check_and_load_module($);
sub genpng_print_usage(*);
sub genpng_process_file($$$$);
sub warn_handler($);
sub die_handler($);


#
# Code entry point
#

# Check whether required module GD.pm is installed
if (check_and_load_module("GD"))
{
	# Note: cannot use die() to print this message because inserting this
	# code into another script via do() would not fail as required!
	print(STDERR <<END_OF_TEXT)
ERROR: required module GD.pm not found on this system (see www.cpan.org).
END_OF_TEXT
	;
	exit(2);
}

# Check whether we're called from the command line or from another script
if (!caller)
{
	my $filename;
	my $tab_size = 4;
	my $width = 80;
	my $out_filename;
	my $help;
	my $version;

	$SIG{__WARN__} = \&warn_handler;
	$SIG{__DIE__} = \&die_handler;

	# Parse command line options
	if (!GetOptions("tab-size=i" => \$tab_size,
			"width=i" => \$width,
			"output-filename=s" => \$out_filename,
			"help" => \$help,
			"version" => \$version))
	{
		print(STDERR "Use $tool_name --help to get usage ".
		      "information\n");
		exit(1);
	}

	$filename = $ARGV[0];

	# Check for help flag
	if ($help)
	{
		genpng_print_usage(*STDOUT);
		exit(0);
	}

	# Check for version flag
	if ($version)
	{
		print("$tool_name: $lcov_version\n");
		exit(0);
	}

	# Check options
	if (!$filename)
	{
		die("No filename specified\n");
	}

	# Check for output filename
	if (!$out_filename)
	{
		$out_filename = "$filename.png";
	}

	genpng_process_file($filename, $out_filename, $width, $tab_size);
	exit(0);
}


#
# genpng_print_usage(handle)
#
# Write out command line usage information to given filehandle.
#

sub genpng_print_usage(*)
{
	local *HANDLE = $_[0];

	print(HANDLE <<END_OF_USAGE)
Usage: $tool_name [OPTIONS] SOURCEFILE

Create an overview image for a given source code file of either plain text
or .gcov file format.

  -h, --help                        Print this help, then exit
  -v, --version                     Print version number, then exit
  -t, --tab-size TABSIZE            Use TABSIZE spaces in place of tab
  -w, --width WIDTH                 Set width of output image to WIDTH pixel
  -o, --output-filename FILENAME    Write image to FILENAME

For more information see: $lcov_url
END_OF_USAGE
	;
}


#
# check_and_load_module(module_name)
#
# Check whether a module by the given name is installed on this system
# and make it known to the interpreter if available. Return undefined if it
# is installed, an error message otherwise.
#

sub check_and_load_module($)
{
	eval("use $_[0];");
	return $@;
}


#
# genpng_process_file(filename, out_filename, width, tab_size)
#

sub genpng_process_file($$$$)
{
	my $filename		= $_[0];
	my $out_filename	= $_[1];
	my $width		= $_[2];
	my $tab_size		= $_[3];
	local *HANDLE;
	my @source;

	open(HANDLE, "<$filename")
		or die("ERROR: cannot open $filename!\n");

	# Check for .gcov filename extension
	if ($filename =~ /^(.*).gcov$/)
	{
		# Assume gcov text format
		while (<HANDLE>)
		{
			if (/^\t\t(.*)$/)
			{
				# Uninstrumented line
				push(@source, ":$1");
			}
			elsif (/^      ######    (.*)$/)
			{
				# Line with zero execution count
				push(@source, "0:$1");
			}
			elsif (/^( *)(\d*)    (.*)$/)
			{
				# Line with positive execution count
				push(@source, "$2:$3");
			}
		}
	}
	else
	{
		# Plain text file
		while (<HANDLE>) { push(@source, ":$_"); }
	}
	close(HANDLE);

	gen_png($out_filename, $width, $tab_size, @source);
}


#
# gen_png(filename, width, tab_size, source)
#
# Write an overview PNG file to FILENAME. Source code is defined by SOURCE
# which is a list of lines <count>:<source code> per source code line.
# The output image will be made up of one pixel per character of source,
# coloring will be done according to execution counts. WIDTH defines the
# image width. TAB_SIZE specifies the number of spaces to use as replacement
# string for tabulator signs in source code text.
#
# Die on error.
#

sub gen_png($$$@)
{
	my $filename = shift(@_);	# Filename for PNG file
	my $overview_width = shift(@_);	# Imagewidth for image
	my $tab_size = shift(@_);	# Replacement string for tab signs
	my @source = @_;	# Source code as passed via argument 2
	my $height = scalar(@source);	# Height as define by source size
	my $overview;		# Source code overview image data
	my $col_plain_back;	# Color for overview background
	my $col_plain_text;	# Color for uninstrumented text
	my $col_cov_back;	# Color for background of covered lines
	my $col_cov_text;	# Color for text of covered lines
	my $col_nocov_back;	# Color for background of lines which
				# were not covered (count == 0)
	my $col_nocov_text;	# Color for test of lines which were not
				# covered (count == 0)
	my $col_hi_back;	# Color for background of highlighted lines
	my $col_hi_text;	# Color for text of highlighted lines
	my $line;		# Current line during iteration
	my $row = 0;		# Current row number during iteration
	my $column;		# Current column number during iteration
	my $color_text;		# Current text color during iteration
	my $color_back;		# Current background color during iteration
	my $last_count;		# Count of last processed line
	my $count;		# Count of current line
	my $source;		# Source code of current line
	my $replacement;	# Replacement string for tabulator chars
	local *PNG_HANDLE;	# Handle for output PNG file

	# Create image
	$overview = new GD::Image($overview_width, $height)
		or die("ERROR: cannot allocate overview image!\n");

	# Define colors
	$col_plain_back	= $overview->colorAllocate(0xff, 0xff, 0xff);
	$col_plain_text	= $overview->colorAllocate(0xaa, 0xaa, 0xaa);
	$col_cov_back	= $overview->colorAllocate(0xaa, 0xa7, 0xef);
	$col_cov_text	= $overview->colorAllocate(0x5d, 0x5d, 0xea);
	$col_nocov_back = $overview->colorAllocate(0xff, 0x00, 0x00);
	$col_nocov_text = $overview->colorAllocate(0xaa, 0x00, 0x00);
	$col_hi_back = $overview->colorAllocate(0x00, 0xff, 0x00);
	$col_hi_text = $overview->colorAllocate(0x00, 0xaa, 0x00);

	# Visualize each line
	foreach $line (@source)
	{
		# Replace tabs with spaces to keep consistent with source
		# code view
		while ($line =~ /^([^\t]*)(\t)/)
		{
			$replacement = " "x($tab_size - ((length($1) - 1) %
				       $tab_size));
			$line =~ s/^([^\t]*)(\t)/$1$replacement/;
		}

		# Skip lines which do not follow the <count>:<line>
		# specification, otherwise $1 = count, $2 = source code
		if (!($line =~ /(\*?)(\d*):(.*)$/)) { next; }
		$count = $2;
		$source = $3;

		# Decide which color pair to use

		# If this line was not instrumented but the one before was,
		# take the color of that line to widen color areas in
		# resulting image
		if (($count eq "") && defined($last_count) &&
		    ($last_count ne ""))
		{
			$count = $last_count;
		}

		if ($count eq "")
		{
			# Line was not instrumented
			$color_text = $col_plain_text;
			$color_back = $col_plain_back;
		}
		elsif ($count == 0)
		{
			# Line was instrumented but not executed
			$color_text = $col_nocov_text;
			$color_back = $col_nocov_back;
		}
		elsif ($1 eq "*")
		{
			# Line was highlighted
			$color_text = $col_hi_text;
			$color_back = $col_hi_back;
		}
		else
		{
			# Line was instrumented and executed
			$color_text = $col_cov_text;
			$color_back = $col_cov_back;
		}

		# Write one pixel for each source character
		$column = 0;
		foreach (split("", $source))
		{
			# Check for width
			if ($column >= $overview_width) { last; }

			if ($_ eq " ")
			{
				# Space
				$overview->setPixel($column++, $row,
						    $color_back);
			}
			else
			{
				# Text
				$overview->setPixel($column++, $row,
						    $color_text);
			}
		}

		# Fill rest of line		
		while ($column < $overview_width)
		{
			$overview->setPixel($column++, $row, $color_back);
		}

		$last_count = $2;

		$row++;
	}

	# Write PNG file
	open (PNG_HANDLE, ">$filename")
		or die("ERROR: cannot write png file $filename!\n");
	binmode(*PNG_HANDLE);
	print(PNG_HANDLE $overview->png());
	close(PNG_HANDLE);
}

sub warn_handler($)
{
	my ($msg) = @_;

	warn("$tool_name: $msg");
}

sub die_handler($)
{
	my ($msg) = @_;

	die("$tool_name: $msg");
}