summaryrefslogtreecommitdiffstats
path: root/src/build/trace/tracepp
blob: bc570aeb846b660c8180f402acd889aa509c7044 (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
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
#!/usr/bin/perl -w
#  IBM_PROLOG_BEGIN_TAG
#  This is an automatically generated prolog.
#
#  $Source: src/build/trace/tracepp $
#
#  IBM CONFIDENTIAL
#
#  COPYRIGHT International Business Machines Corp. 2011 - 2012
#
#  p1
#
#  Object Code Only (OCO) source materials
#  Licensed Internal Code Source Materials
#  IBM HostBoot Licensed Internal Code
#
#  The source code for this program is not published or other-
#  wise divested of its trade secrets, irrespective of what has
#  been deposited with the U.S. Copyright Office.
#
#  Origin: 30
#
#  IBM_PROLOG_END
use strict;
use File::Basename;

# *** tracepp - a fsp/common Linux trace pre processor
# this one replaces the trace strings by the corresponding hash value
# (i.e. the complete call to trace_adal_hash is replaced)

# *** Usage
#
# prepend compiler call with the call of this pre processor, i.e if you have
#      $(CC) $(CFLAGS) -o $@ $<
# in your Makefile change it to this:
#      tracepp $(CC) $(CFLAGS) -o $@ $<
# tracepp will use "$(CC) -E" to call the C pre processor "cpp". 
# you can set a env var "REALCPP" to the name of a program to select
# a different programm as cpp
#
# tracepp creates a file "$target.hash" with the trace strings and the hash values.
#
# to enable debug mode set envvar TRACEPPDEBUG to 1 or give '-d' as first arg

# *** Change History
#
# 2003-02-26  RBa  created from scratch
# 2003-02-28  RBa  add C++ support (C++ interface uses own type for the hash)
# 2003-05-28  RBa  if cc should link instead of compile just call compiler
# 2003-07-11  AGe  Change search alg. slightly and put just format back
# 2003-07-25  RBa  just call gcc if called to link instead to compile
#                  eat argument for option -x
# 2003-11-26  RBa  fix c/c++ algo: compile as c++ if realcc=*g++
# 2004-02-02  RBa  remove explicit test whether source file is readable
#                  it is obsolete and might lead to an error if afs is used
# 2004-02-13  RBa  add support for dependency generation (-MD/-MG, -MF)
#                  don't prepend './' to object filename
# 2006-04-19  RBa  rewrite trace_adal_write_all support, handle C and C++ the same
# 2006-05-24  RBa  fix handling of missing -o ; add TRAC_PPVER for macro/API version
# 2006-09-15  RBa  add handling of \" in trace format strings ; reduce non-error output
#                  put object file in current dir if no -o given
# 2007-03-22  RBa  handle more gcc options (-i) ; protect " in call to shell
#                  store output of cpp as "unmodified" output for debug
#                  only write string/hash file if strings found

my $debug = 0;
if (exists $ENV{TRACEPPDEBUG}) {
	$debug = $ENV{TRACEPPDEBUG};
}

my $version = '$Id: tracepp,v 1.8 2007/06/22 13:37:17 fldbuild Exp $';
$version =~ s/^.*(\d+(\.\d+)+).*$/$1/;
# api/macro version. to #error if macro and tracepp doesn't fit
my $macro_version = '1';

sub parse_line($$);
sub get_has($$);

if (@ARGV == 0 || (@ARGV == 1 && lc($ARGV[0]) eq '-h') ) {
	print STDERR "usage: $0 realcompiler compileroptions -o target source\n";
	exit 127;
}
my $realcc = shift @ARGV;
my $cctype = 'c++';
my $optx_found = 0;

if ($realcc eq '-d') {
	$debug = 1;
	$realcc = shift @ARGV;
}

# wait until -d options is handled before checking $debug
print STDERR "tracepp version $version - API/macro version $macro_version\n" if $debug;

my $realcpp = $ENV{REALCPP};
if (!$realcpp) {
	print STDERR "cannot find cpp, using \<realcompiler\> -E'\n" if $debug;
	$realcpp = "$realcc -E";
}
print "realcpp is $realcpp\n" if $debug;

my $opt;
my ($source, $object, @ccopts, @cppopts);
my $source_basename;
my $dodeps = 0;
my $depfile;
my @origargs = @ARGV;
while(defined($opt = shift @ARGV)) {
	if ($opt =~ m/^-o(.*)$/) {
		if (defined $object) {
			print STDERR "two -o options, aborting\n";
			exit 1;
		}
		if ($1) {
			$object = $1;
		} else {
			$object = shift @ARGV;
		}
		print "object is now $object\n" if $debug;
	} elsif ($opt eq '-c') {
		# don't call cpp with -c, this is for the compiler
		#push @cppopts, $opt;
		push @ccopts, $opt;
		print "found -c option\n" if $debug;
	} elsif ($opt =~ m/^-l/) {
		# cpp doesn't need library arguments
		push @ccopts, $opt;
	} elsif ($opt =~ m/^-i./) {
		# option takes an argument, handle it too
		my$optarg = shift @ARGV;
		push @ccopts, $opt, $optarg;
		push @cppopts, $opt, $optarg;
		print "found option '$opt $optarg'\n" if $debug;
	} elsif ($opt =~ m/^-[LIxbBVD]$/ || $opt eq '--param' || $opt =~ m/^-M[QT]$/) {
		# option takes an argument, handle it too
		my $optarg = shift @ARGV;
		push @ccopts, $opt, $optarg;
		push @cppopts, $opt, $optarg;
		print "found option '$opt $optarg'\n" if $debug;
		if ($opt eq '-x') {
			# option x sets the language - c or c++
			if ($optarg ne 'c' and $optarg ne 'c++') {
				print STDERR "cannot process language '$optarg', aborting\n";
				exit 1;
			}
			$cctype = $optarg;
			$optx_found = 1;
		}
	} elsif ($opt eq '-MD' or $opt eq '-MG') {
		# gen deps
		$dodeps = 1;
		print "found $opt, creating dependencies\n" if $debug;
	} elsif ($opt eq '-MF') {
		# set dependencies file
		$depfile = shift @ARGV;
		print "set dependencies file to '$depfile'\n" if $debug;
	} elsif ($opt =~ m/^-/) {
		# arg starts with - so it's an option
		push @ccopts, $opt;
		push @cppopts, $opt;
		print "found option '$opt'\n" if $debug;
	} elsif ($opt =~ m/\.[ao]$/) {
		# an object or archive, ignore this but give it to cc
		push @ccopts, $opt;
		print "found object/archive '$opt'\n" if $debug;
	} elsif ($opt =~ m/\.c[cxp]*$/i) {
		# the source file(s). we should only get one
		if (defined $source) {
			print STDERR "don't know to handle two source files, aborting\n";
			exit 1;
		}
		$source = $opt;
                $source_basename = basename($source).": ";
		# put the - (for read-from-stdin) where the source file was
		# (order is important!)
		push @ccopts, "-";
		print "found source file $source\n" if $debug;
	} elsif (!-f $opt) {
		# option but not a file, an unknown option?
		push @ccopts, $opt;
		push @cppopts, $opt;
		print "found unknown option '$opt'\n" if $debug;
	}
}

if (!defined $source) {
	# this might be a call to link a program instead of compile a source (or asm source)
	print "NOTME: starting as cc '$realcc @origargs'\n" if $debug;
	exec($realcc, @origargs) || die "cannot exec $realcc\n";
}
if (!defined $object) {
	print STDERR "no object file given, default to source name\n" if $debug;
	# gcc builds object name from source name if no -o given, replacing 
	# suffix with .o. The file is placed in the current directory, 
	# not in the source directory!
	my ($n,$d,$s) = fileparse($source, qr{\.[^.]+});
	if ($n && $s) {
		$object = "$n.o";
		print "tracpp: guessing object name $object\n",
		      "        from source name     $source\n" if $debug;
	} else {
		print STDERR "Unable to determine Source File Name\n";
		exit 1;
	}
}
my $hashtype;
# set value of trace hash according to language
# check source file extension if no explicit -x option given
if (!$optx_found) {
	if ($realcc =~ m/g\+\+/) {
		print "compiler language: C++ (from compiler name)\n" if $debug;
		$cctype = 'c++';
	} else {
		if ($source =~ m/\.c$/) {
			print "compiler language: C (from source file extension)\n"
				if $debug;
			$cctype = 'c';
		} else {
			print "compiler language: C++ (default)\n" if $debug;
			$cctype = 'c++';
		}
	}
} else {
	print "compiler language: $cctype (from option '-x')\n" if $debug;
}

if ($cctype eq 'c') {
	$hashtype = 'unsigned long';
} else {
	$hashtype = 'trace_hash_val';
}
# define TRAC_TRACEPP for macros
push(@cppopts,"-DTRAC_TRACEPP -DTRAC_PPVER=$macro_version");
if ($dodeps) {
	if (!defined $depfile) {
		if (exists $ENV{DEPENDENCIES_OUTPUT}) {
			$depfile = $ENV{DEPENDENCIES_OUTPUT};
		} elsif (exists $ENV{SUNPRO_DEPENDENCIES}) {
			$depfile = $ENV{SUNPRO_DEPENDENCIES};
		} else {
			($depfile = $object) =~ s/.o$/.d/;
		}
	}
	push @cppopts, "-MD -MF $depfile";
}
# start cpp.
print "starting as cpp '$realcpp @cppopts $source -o-'\n" if $debug;
if (!open(CPP, "$realcpp @cppopts $source -o-|")) {
	print STDERR "cannot start cpp '$realcpp'\n";
	perror("");
	exit 1;
}
# start cc. manually set language as source file extension not available to cc
my $type_str = '';
if ($optx_found == 0) {
	# no option -x given by caller, set manually
	$type_str = "-x $cctype";
}
print "starting as cc '$realcc $type_str @ccopts -o $object'\n" if $debug;
if (!open(CC, "| $realcc $type_str @ccopts -o $object")) {
	print STDERR "cannot start cc '$realcc'\n";
	perror("");
	exit 1;
}

my $modifiedfile = 0;
my $unmodifiedfile = 0;
if ($debug) {
	$modifiedfile = $object . ".debug";
	if (!open(DEBUG, ">$modifiedfile")) {
		perror("cannot open file $modifiedfile");
		$modifiedfile = 0;
	} else {
		print STDERR "writing preprocessed source to $modifiedfile\n";
	}
	$unmodifiedfile = $object . ".debug_in";
	if (!open(DEBUGIN, ">$unmodifiedfile")) {
		perror("cannot open file $unmodifiedfile");
		$unmodifiedfile = 0;
	} else {
		print STDERR "writing unprocessed source to $unmodifiedfile\n";
	}
}

my %hashtab;
my $oldline;
while(defined($oldline = <CPP>)) {
	print DEBUGIN $oldline if $unmodifiedfile;
	my $newline = parse_line(\%hashtab, $oldline);
	#print "oldline = $oldline";
	#print "newline = $newline";
	if (!defined $newline) {
		print STDERR "hash error in/with file $source\n";
		exit 1;
	}
	#print "newline = $newline\n";
	print CC $newline;
	print DEBUG $newline if $modifiedfile;
}
if ($modifiedfile) {
	close DEBUG;
}
if ($unmodifiedfile) {
	close DEBUGIN;
}
if (!close(CPP) || ($? >> 8 != 0)) {
	print STDERR "error from cpp\n";
	if ($? & 127) {
		print STDERR "cpp got signal ",$? & 127,"\n";
		exit 1;
	} elsif ($? >> 8) {
		print STDERR "cpp returned ",$? >> 8,"\n";
		exit $? >> 8;
	}
}
if (!close(CC) || ($? >> 8 != 0)) {
	print STDERR "error from cc\n";
	if ($? & 127) {
		print STDERR "cc got signal ",$? & 127,"\n";
		exit 1;
	} elsif ($? >> 8) {
		print STDERR "cc returned ",$? >> 8,"\n";
		exit $? >> 8;
	}
}
if (%hashtab) {
	my $stringfile = "$object.hash";
	# open trace string file
	if (!open(TRC, ">$stringfile")) {
		print STDERR "cannot write trace string file '$stringfile'\n";
		exit 1;
	}
	print "Writing to file $stringfile\n" if $debug;

	printf TRC "#FSP_TRACE_v2|||%s|||BUILD:%s",scalar(localtime()),`pwd`;
	foreach my $key (keys %hashtab) {
		if ($hashtab{$key} =~ m/\|\|$source$/) {
			# source file name is already part of the string
			print TRC "$key||$hashtab{$key}\n";
		} else {
			print TRC "$key||$hashtab{$key}||$source\n";
		}
		#print TRC "$key||$source||$hashtab{$key}\n";
	}
	close TRC;
} else {
	print "No trace calls/strings found, not writing hash file\n" if $debug;
}
exit 0;

sub parse_line($$)
{
	my ($rhash, $line) = @_;
	my $format;
	my $tmp_strng;
	my @format_param = ();
	my $data;
	my $hash;
	my $newline = '';
	my $write_all_suffix;
	my ($prefix, $strings, $salt, $suffix);
	# trace_adal_hash ( "..." ".." "..." , 2 )
	# regex: PREFIX 'trace_adal_hash' space '(' space STRINGS  space ',' space NUMBER space ')' SUFFIX
	# STRINGS:  '"' .* '"' space? +
	while($line =~ m/^(.*?)trace_adal_hash\s*\(\s*((".*?(?<!\\)"\s*)+),\s*(-?\d+)\s*\)(.*)$/) {
		($prefix, $strings, $salt, $suffix) = ($1, $2, $4, $5);
		print "\n\nprefix = $prefix\nstrings = $strings\nsalt = $salt\nsuffix = $suffix\n" if $debug;
                $strings =~ s/TRACEPP_INSERT_FILENAME/$source_basename/;
		$strings =~ s/^"//;
		$strings =~ s/"\s*$//;
		$strings =~ s/"\s*"//g;
		$strings =~ s/\\"/"/g; # remove \ from \"
		# is this a trace_adal_write_all call?
		if ($prefix =~ m/trace_adal_write_all\s*\(/) {
			# yes. replace trace_adal_hash with hash value and reduced format string
			(@format_param) = ($strings =~ /(%[#0\- +'I]*\d*(?:\.\d*)?[hlLqjzt]*[diouxXeEfFgGaAcsCSpn])/g);
			$format = join(',', @format_param);
			# reduced format string will be added after hash value
			$write_all_suffix = '," ' . $format . '"';
			if ($salt == -1) {
				$salt = scalar(@format_param);
			} elsif ($salt != scalar(@format_param)) {
				print STDERR ("printf % mismatch in '$line': TRACE says $salt, format says ",
					scalar(@format_param), " args\n");
			}
		} else {
			$write_all_suffix = '';
		}
		$hash = get_hash($strings, $salt);
		if (exists $$rhash{$hash} && $$rhash{$hash} ne $strings) {
			print STDERR "hash collision: two different strings give the same hash value '$hash'\n",
				$strings, "\n", $$rhash{$hash}, "\n";
			return undef;
		}
		$$rhash{$hash} = $strings;
		$newline .= $prefix . "(($hashtype) ${hash}U)$write_all_suffix";
		print "changed call: $prefix(($hashtype) ${hash}U)$write_all_suffix...\n" if $debug;
		$line = $suffix;
	}
	$newline .= $line if $line;
	$newline .= "\n" unless $newline =~ m/\n$/;
	return $newline;
}

sub get_hash($$)
{
	my ($string, $salt) = @_;
	$string =~ s/([\\"])/\\$1/g;
	my $hash = `trexhash "$string" $salt`;
	chomp $hash;
	if ($hash !~ m/^\d+$/) {
		print STDERR "trexhash error: $hash\n";
		print STDERR "for call <<trexhash \"$string\" $salt>>\n";
		die "$!";
	}
	if (!$hash) {
		for (my $i=0; $i < length($string); $i++) {
			$hash += ord(substr($string, $i, 1));
		}
	}
	return $hash;
}
OpenPOWER on IntegriCloud