xref: /titanic_44/usr/src/tools/scripts/wsdiff.py (revision ccac5ae384bd76b9eb3473b622170555228b5c5d)
1598cc7dfSVladimir Kotal#!/usr/bin/python2.6
296ccc8cbSesaxe#
396ccc8cbSesaxe# CDDL HEADER START
496ccc8cbSesaxe#
596ccc8cbSesaxe# The contents of this file are subject to the terms of the
696ccc8cbSesaxe# Common Development and Distribution License (the "License").
796ccc8cbSesaxe# You may not use this file except in compliance with the License.
896ccc8cbSesaxe#
996ccc8cbSesaxe# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
1096ccc8cbSesaxe# or http://www.opensolaris.org/os/licensing.
1196ccc8cbSesaxe# See the License for the specific language governing permissions
1296ccc8cbSesaxe# and limitations under the License.
1396ccc8cbSesaxe#
1496ccc8cbSesaxe# When distributing Covered Code, include this CDDL HEADER in each
1596ccc8cbSesaxe# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1696ccc8cbSesaxe# If applicable, add the following below this CDDL HEADER, with the
1796ccc8cbSesaxe# fields enclosed by brackets "[]" replaced with your own identifying
1896ccc8cbSesaxe# information: Portions Copyright [yyyy] [name of copyright owner]
1996ccc8cbSesaxe#
2096ccc8cbSesaxe# CDDL HEADER END
2196ccc8cbSesaxe#
22598cc7dfSVladimir Kotal# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
2396ccc8cbSesaxe#
2496ccc8cbSesaxe
2596ccc8cbSesaxe#
2696ccc8cbSesaxe# wsdiff(1) is a tool that can be used to determine which compiled objects
2796ccc8cbSesaxe# have changed as a result of a given source change. Developers backporting
2896ccc8cbSesaxe# new features, RFEs and bug fixes need to be able to identify the set of
2996ccc8cbSesaxe# patch deliverables necessary for feature/fix realization on a patched system.
3096ccc8cbSesaxe#
3196ccc8cbSesaxe# The tool works by comparing objects in two trees/proto areas (one build with,
3296ccc8cbSesaxe# and without the source changes.
3396ccc8cbSesaxe#
3496ccc8cbSesaxe# Using wsdiff(1) is fairly simple:
3596ccc8cbSesaxe#	- Bringover to a fresh workspace
3696ccc8cbSesaxe#	- Perform a full non-debug build (clobber if workspace isn't fresh)
3796ccc8cbSesaxe#	- Move the proto area aside, call it proto.old, or something.
3896ccc8cbSesaxe#	- Integrate your changes to the workspace
3996ccc8cbSesaxe#	- Perform another full non-debug clobber build.
4096ccc8cbSesaxe#	- Use wsdiff(1) to see what changed:
4196ccc8cbSesaxe#		$ wsdiff proto.old proto
4296ccc8cbSesaxe#
4396ccc8cbSesaxe# By default, wsdiff will print the list of changed objects / deliverables to
4496ccc8cbSesaxe# stdout. If a results file is specified via -r, the list of differing objects,
4596ccc8cbSesaxe# and details about why wsdiff(1) thinks they are different will be logged to
4696ccc8cbSesaxe# the results file.
4796ccc8cbSesaxe#
4896ccc8cbSesaxe# By invoking nightly(1) with the -w option to NIGHTLY_FLAGS, nightly(1) will use
4996ccc8cbSesaxe# wsdiff(1) to report on what objects changed since the last build.
5096ccc8cbSesaxe#
5196ccc8cbSesaxe# For patch deliverable purposes, it's advised to have nightly do a clobber,
5296ccc8cbSesaxe# non-debug build.
5396ccc8cbSesaxe#
5496ccc8cbSesaxe# Think about the results. Was something flagged that you don't expect? Go look
5596ccc8cbSesaxe# at the results file to see details about the differences.
5696ccc8cbSesaxe#
5796ccc8cbSesaxe# Use the -i option in conjunction with -v and -V to dive deeper and have wsdiff(1)
5896ccc8cbSesaxe# report with more verbosity.
5996ccc8cbSesaxe#
6096ccc8cbSesaxe# Usage: wsdiff [-vVt] [-r results ] [-i filelist ] old new
6196ccc8cbSesaxe#
6296ccc8cbSesaxe# Where "old" is the path to the proto area build without the changes, and
6396ccc8cbSesaxe# "new" is the path to the proto area built with the changes. The following
6496ccc8cbSesaxe# options are supported:
6596ccc8cbSesaxe#
6696ccc8cbSesaxe#        -v      Do not truncate observed diffs in results
6796ccc8cbSesaxe#        -V      Log *all* ELF sect diffs vs. logging the first diff found
6896ccc8cbSesaxe#        -t      Use onbld tools in $SRC/tools
6996ccc8cbSesaxe#        -r      Log results and observed differences
7096ccc8cbSesaxe#        -i      Tell wsdiff which objects to compare via an input file list
7196ccc8cbSesaxe
72598cc7dfSVladimir Kotalimport datetime, fnmatch, getopt, os, profile, commands
73598cc7dfSVladimir Kotalimport re, resource, select, shutil, signal, string, struct, sys, tempfile
74598cc7dfSVladimir Kotalimport time, threading
7596ccc8cbSesaxefrom stat import *
7696ccc8cbSesaxe
7796ccc8cbSesaxe# Human readable diffs truncated by default if longer than this
7896ccc8cbSesaxe# Specifying -v on the command line will override
7996ccc8cbSesaxediffs_sz_thresh = 4096
8096ccc8cbSesaxe
81598cc7dfSVladimir Kotal# Lock name	 Provides exclusive access to
82598cc7dfSVladimir Kotal# --------------+------------------------------------------------
83598cc7dfSVladimir Kotal# output_lock	 standard output or temporary file (difference())
84598cc7dfSVladimir Kotal# log_lock	 the results file (log_difference())
85598cc7dfSVladimir Kotal# wset_lock	 changedFiles list (workerThread())
86598cc7dfSVladimir Kotaloutput_lock = threading.Lock()
87598cc7dfSVladimir Kotallog_lock = threading.Lock()
88598cc7dfSVladimir Kotalwset_lock = threading.Lock()
89598cc7dfSVladimir Kotal
90598cc7dfSVladimir Kotal# Variable for thread control
91598cc7dfSVladimir Kotalkeep_processing = True
92598cc7dfSVladimir Kotal
9396ccc8cbSesaxe# Default search path for wsdiff
9496ccc8cbSesaxewsdiff_path = [ "/usr/bin",
9596ccc8cbSesaxe		"/usr/ccs/bin",
9696ccc8cbSesaxe		"/lib/svc/bin",
9796ccc8cbSesaxe		"/opt/onbld/bin" ]
9896ccc8cbSesaxe
9996ccc8cbSesaxe# These are objects that wsdiff will notice look different, but will not report.
10096ccc8cbSesaxe# Existence of an exceptions list, and adding things here is *dangerous*,
10196ccc8cbSesaxe# and therefore the *only* reasons why anything would be listed here is because
10296ccc8cbSesaxe# the objects do not build deterministically, yet we *cannot* fix this.
10396ccc8cbSesaxe#
10496ccc8cbSesaxe# These perl libraries use __DATE__ and therefore always look different.
10596ccc8cbSesaxe# Ideally, we would purge use the use of __DATE__ from the source, but because
10696ccc8cbSesaxe# this is source we wish to distribute with Solaris "unchanged", we cannot modify.
10796ccc8cbSesaxe#
10896ccc8cbSesaxewsdiff_exceptions = [ "usr/perl5/5.8.4/lib/sun4-solaris-64int/CORE/libperl.so.1",
10996ccc8cbSesaxe		      "usr/perl5/5.6.1/lib/sun4-solaris-64int/CORE/libperl.so.1",
11096ccc8cbSesaxe		      "usr/perl5/5.8.4/lib/i86pc-solaris-64int/CORE/libperl.so.1",
11196ccc8cbSesaxe		      "usr/perl5/5.6.1/lib/i86pc-solaris-64int/CORE/libperl.so.1"
11296ccc8cbSesaxe		      ]
11396ccc8cbSesaxe
11496ccc8cbSesaxe#####
11596ccc8cbSesaxe# Logging routines
11696ccc8cbSesaxe#
11796ccc8cbSesaxe
118598cc7dfSVladimir Kotal# Debug message to be printed to the screen, and the log file
119598cc7dfSVladimir Kotaldef debug(msg) :
120598cc7dfSVladimir Kotal
121598cc7dfSVladimir Kotal	# Add prefix to highlight debugging message
122598cc7dfSVladimir Kotal	msg = "## " + msg
123598cc7dfSVladimir Kotal	if debugon :
124598cc7dfSVladimir Kotal		output_lock.acquire()
125598cc7dfSVladimir Kotal		print >> sys.stdout, msg
126598cc7dfSVladimir Kotal		sys.stdout.flush()
127598cc7dfSVladimir Kotal		output_lock.release()
128598cc7dfSVladimir Kotal		if logging :
129598cc7dfSVladimir Kotal			log_lock.acquire()
130598cc7dfSVladimir Kotal			print >> log, msg
131598cc7dfSVladimir Kotal			log.flush()
132598cc7dfSVladimir Kotal			log_lock.release()
133598cc7dfSVladimir Kotal
13496ccc8cbSesaxe# Informational message to be printed to the screen, and the log file
13596ccc8cbSesaxedef info(msg) :
13696ccc8cbSesaxe
137598cc7dfSVladimir Kotal	output_lock.acquire()
13896ccc8cbSesaxe	print >> sys.stdout, msg
13996ccc8cbSesaxe	sys.stdout.flush()
140598cc7dfSVladimir Kotal	output_lock.release()
141598cc7dfSVladimir Kotal	if logging :
142598cc7dfSVladimir Kotal		log_lock.acquire()
143598cc7dfSVladimir Kotal		print >> log, msg
144598cc7dfSVladimir Kotal		log.flush()
145598cc7dfSVladimir Kotal		log_lock.release()
14696ccc8cbSesaxe
14796ccc8cbSesaxe# Error message to be printed to the screen, and the log file
14896ccc8cbSesaxedef error(msg) :
14996ccc8cbSesaxe
150598cc7dfSVladimir Kotal	output_lock.acquire()
15196ccc8cbSesaxe	print >> sys.stderr, "ERROR:", msg
15296ccc8cbSesaxe	sys.stderr.flush()
153598cc7dfSVladimir Kotal	output_lock.release()
15496ccc8cbSesaxe	if logging :
155598cc7dfSVladimir Kotal		log_lock.acquire()
15696ccc8cbSesaxe		print >> log, "ERROR:", msg
15796ccc8cbSesaxe		log.flush()
158598cc7dfSVladimir Kotal		log_lock.release()
15996ccc8cbSesaxe
16096ccc8cbSesaxe# Informational message to be printed only to the log, if there is one.
16196ccc8cbSesaxedef v_info(msg) :
16296ccc8cbSesaxe
16396ccc8cbSesaxe	if logging :
164598cc7dfSVladimir Kotal		log_lock.acquire()
16596ccc8cbSesaxe		print >> log, msg
16696ccc8cbSesaxe		log.flush()
167598cc7dfSVladimir Kotal		log_lock.release()
16896ccc8cbSesaxe
16996ccc8cbSesaxe#
17096ccc8cbSesaxe# Flag a detected file difference
17196ccc8cbSesaxe# Display the fileName to stdout, and log the difference
17296ccc8cbSesaxe#
17396ccc8cbSesaxedef difference(f, dtype, diffs) :
17496ccc8cbSesaxe
17596ccc8cbSesaxe	if f in wsdiff_exceptions :
17696ccc8cbSesaxe		return
17796ccc8cbSesaxe
178598cc7dfSVladimir Kotal	output_lock.acquire()
179598cc7dfSVladimir Kotal	if sorted :
180598cc7dfSVladimir Kotal		differentFiles.append(f)
181598cc7dfSVladimir Kotal	else:
18296ccc8cbSesaxe		print >> sys.stdout, f
18396ccc8cbSesaxe		sys.stdout.flush()
184598cc7dfSVladimir Kotal	output_lock.release()
18596ccc8cbSesaxe
18696ccc8cbSesaxe	log_difference(f, dtype, diffs)
18796ccc8cbSesaxe
18896ccc8cbSesaxe#
18996ccc8cbSesaxe# Do the actual logging of the difference to the results file
19096ccc8cbSesaxe#
19196ccc8cbSesaxedef log_difference(f, dtype, diffs) :
192598cc7dfSVladimir Kotal
19396ccc8cbSesaxe	if logging :
194598cc7dfSVladimir Kotal		log_lock.acquire()
19596ccc8cbSesaxe		print >> log, f
19696ccc8cbSesaxe		print >> log, "NOTE:", dtype, "difference detected."
19796ccc8cbSesaxe
19896ccc8cbSesaxe		difflen = len(diffs)
19996ccc8cbSesaxe		if difflen > 0 :
20096ccc8cbSesaxe			print >> log
20196ccc8cbSesaxe
20296ccc8cbSesaxe			if not vdiffs and difflen > diffs_sz_thresh :
20396ccc8cbSesaxe				print >> log, diffs[:diffs_sz_thresh]
20496ccc8cbSesaxe				print >> log, \
20596ccc8cbSesaxe				      "... truncated due to length: " \
20696ccc8cbSesaxe				      "use -v to override ..."
20796ccc8cbSesaxe			else :
20896ccc8cbSesaxe				print >> log, diffs
20996ccc8cbSesaxe			print >> log, "\n"
21096ccc8cbSesaxe		log.flush()
211598cc7dfSVladimir Kotal		log_lock.release()
21296ccc8cbSesaxe
21396ccc8cbSesaxe
21496ccc8cbSesaxe#####
21596ccc8cbSesaxe# diff generating routines
21696ccc8cbSesaxe#
21796ccc8cbSesaxe
21896ccc8cbSesaxe#
21996ccc8cbSesaxe# Return human readable diffs from two temporary files
22096ccc8cbSesaxe#
22196ccc8cbSesaxedef diffFileData(tmpf1, tmpf2) :
22296ccc8cbSesaxe
223598cc7dfSVladimir Kotal	binaries = False
224598cc7dfSVladimir Kotal
22596ccc8cbSesaxe	# Filter the data through od(1) if the data is detected
22696ccc8cbSesaxe	# as being binary
22796ccc8cbSesaxe	if isBinary(tmpf1) or isBinary(tmpf2) :
228598cc7dfSVladimir Kotal		binaries = True
22996ccc8cbSesaxe		tmp_od1 = tmpf1 + ".od"
23096ccc8cbSesaxe		tmp_od2 = tmpf2 + ".od"
23196ccc8cbSesaxe
23296ccc8cbSesaxe		cmd = od_cmd + " -c -t x4" + " " + tmpf1 + " > " + tmp_od1
23396ccc8cbSesaxe		os.system(cmd)
23496ccc8cbSesaxe		cmd = od_cmd + " -c -t x4" + " " + tmpf2 + " > " + tmp_od2
23596ccc8cbSesaxe		os.system(cmd)
23696ccc8cbSesaxe
23796ccc8cbSesaxe		tmpf1 = tmp_od1
23896ccc8cbSesaxe		tmpf2 = tmp_od2
23996ccc8cbSesaxe
240598cc7dfSVladimir Kotal	try:
24196ccc8cbSesaxe		data = commands.getoutput(diff_cmd + " " + tmpf1 + " " + tmpf2)
242598cc7dfSVladimir Kotal		# Remove the temp files as we no longer need them.
243598cc7dfSVladimir Kotal		if binaries :
244598cc7dfSVladimir Kotal			try:
245598cc7dfSVladimir Kotal				os.unlink(tmp_od1)
246598cc7dfSVladimir Kotal			except OSError, e:
247598cc7dfSVladimir Kotal				error("diffFileData: unlink failed %s" % e)
248598cc7dfSVladimir Kotal			try:
249598cc7dfSVladimir Kotal				os.unlink(tmp_od2)
250598cc7dfSVladimir Kotal			except OSError, e:
251598cc7dfSVladimir Kotal				error("diffFileData: unlink failed %s" % e)
252598cc7dfSVladimir Kotal	except:
253598cc7dfSVladimir Kotal		error("failed to get output of command: " + diff_cmd + " " \
254598cc7dfSVladimir Kotal		    + tmpf1 + " " + tmpf2)
255598cc7dfSVladimir Kotal
256598cc7dfSVladimir Kotal		# Send exception for the failed command up
257598cc7dfSVladimir Kotal		raise
258598cc7dfSVladimir Kotal		return
25996ccc8cbSesaxe
26096ccc8cbSesaxe	return data
26196ccc8cbSesaxe
26296ccc8cbSesaxe#
26396ccc8cbSesaxe# Return human readable diffs betweeen two datasets
26496ccc8cbSesaxe#
265598cc7dfSVladimir Kotaldef diffData(base, ptch, d1, d2) :
26696ccc8cbSesaxe
267598cc7dfSVladimir Kotal	t = threading.currentThread()
268598cc7dfSVladimir Kotal	tmpFile1 = tmpDir1 + os.path.basename(base) + t.getName()
269598cc7dfSVladimir Kotal	tmpFile2 = tmpDir2 + os.path.basename(ptch) + t.getName()
27096ccc8cbSesaxe
27196ccc8cbSesaxe	try:
27296ccc8cbSesaxe		fd1 = open(tmpFile1, "w")
27396ccc8cbSesaxe	except:
27496ccc8cbSesaxe		error("failed to open: " + tmpFile1)
27596ccc8cbSesaxe		cleanup(1)
276598cc7dfSVladimir Kotal
27796ccc8cbSesaxe	try:
27896ccc8cbSesaxe		fd2 = open(tmpFile2, "w")
27996ccc8cbSesaxe	except:
28096ccc8cbSesaxe		error("failed to open: " + tmpFile2)
28196ccc8cbSesaxe		cleanup(1)
28296ccc8cbSesaxe
28396ccc8cbSesaxe	fd1.write(d1)
28496ccc8cbSesaxe	fd2.write(d2)
28596ccc8cbSesaxe	fd1.close()
28696ccc8cbSesaxe	fd2.close()
28796ccc8cbSesaxe
28896ccc8cbSesaxe	return diffFileData(tmpFile1, tmpFile2)
28996ccc8cbSesaxe
29096ccc8cbSesaxe#####
29196ccc8cbSesaxe# Misc utility functions
29296ccc8cbSesaxe#
29396ccc8cbSesaxe
29496ccc8cbSesaxe# Prune off the leading prefix from string s
29596ccc8cbSesaxedef str_prefix_trunc(s, prefix) :
29696ccc8cbSesaxe	snipLen = len(prefix)
29796ccc8cbSesaxe	return s[snipLen:]
29896ccc8cbSesaxe
29996ccc8cbSesaxe#
30096ccc8cbSesaxe# Prune off leading proto path goo (if there is one) to yield
30196ccc8cbSesaxe# the deliverable's eventual path relative to root
30296ccc8cbSesaxe# e.g. proto.base/root_sparc/usr/src/cmd/prstat => usr/src/cmd/prstat
30396ccc8cbSesaxe#
30496ccc8cbSesaxedef fnFormat(fn) :
30596ccc8cbSesaxe	root_arch_str = "root_" + arch
30696ccc8cbSesaxe
30796ccc8cbSesaxe	pos = fn.find(root_arch_str)
30896ccc8cbSesaxe	if pos == -1 :
30996ccc8cbSesaxe		return fn
31096ccc8cbSesaxe
31196ccc8cbSesaxe	pos = fn.find("/", pos)
31296ccc8cbSesaxe	if pos == -1 :
31396ccc8cbSesaxe		return fn
31496ccc8cbSesaxe
31596ccc8cbSesaxe	return fn[pos + 1:]
31696ccc8cbSesaxe
31796ccc8cbSesaxe#####
31896ccc8cbSesaxe# Usage / argument processing
31996ccc8cbSesaxe#
32096ccc8cbSesaxe
32196ccc8cbSesaxe#
32296ccc8cbSesaxe# Display usage message
32396ccc8cbSesaxe#
32496ccc8cbSesaxedef usage() :
32596ccc8cbSesaxe	sys.stdout.flush()
326598cc7dfSVladimir Kotal	print >> sys.stderr, """Usage: wsdiff [-dvVst] [-r results ] [-i filelist ] old new
327598cc7dfSVladimir Kotal        -d      Print debug messages about the progress
32896ccc8cbSesaxe        -v      Do not truncate observed diffs in results
32996ccc8cbSesaxe        -V      Log *all* ELF sect diffs vs. logging the first diff found
33096ccc8cbSesaxe        -t      Use onbld tools in $SRC/tools
33196ccc8cbSesaxe        -r      Log results and observed differences
332598cc7dfSVladimir Kotal        -s      Produce sorted list of differences
33396ccc8cbSesaxe        -i      Tell wsdiff which objects to compare via an input file list"""
33496ccc8cbSesaxe	sys.exit(1)
33596ccc8cbSesaxe
33696ccc8cbSesaxe#
33796ccc8cbSesaxe# Process command line options
33896ccc8cbSesaxe#
33996ccc8cbSesaxedef args() :
34096ccc8cbSesaxe
341598cc7dfSVladimir Kotal	global debugon
34296ccc8cbSesaxe	global logging
34396ccc8cbSesaxe	global vdiffs
34496ccc8cbSesaxe	global reportAllSects
345598cc7dfSVladimir Kotal	global sorted
34696ccc8cbSesaxe
347598cc7dfSVladimir Kotal	validOpts = 'di:r:vVst?'
34896ccc8cbSesaxe
34996ccc8cbSesaxe	baseRoot = ""
35096ccc8cbSesaxe	ptchRoot = ""
35196ccc8cbSesaxe	fileNamesFile = ""
35296ccc8cbSesaxe	results = ""
35396ccc8cbSesaxe	localTools = False
35496ccc8cbSesaxe
35596ccc8cbSesaxe	# getopt.getopt() returns:
35696ccc8cbSesaxe	#	an option/value tuple
35796ccc8cbSesaxe	#	a list of remaining non-option arguments
35896ccc8cbSesaxe	#
35996ccc8cbSesaxe	# A correct wsdiff invocation will have exactly two non option
36096ccc8cbSesaxe	# arguments, the paths to the base (old), ptch (new) proto areas
36196ccc8cbSesaxe	try:
36296ccc8cbSesaxe		optlist, args = getopt.getopt(sys.argv[1:], validOpts)
36396ccc8cbSesaxe	except getopt.error, val:
36496ccc8cbSesaxe		usage()
36596ccc8cbSesaxe
36696ccc8cbSesaxe	if len(args) != 2 :
36796ccc8cbSesaxe		usage();
36896ccc8cbSesaxe
36996ccc8cbSesaxe	for opt,val in optlist :
370598cc7dfSVladimir Kotal		if opt == '-d' :
371598cc7dfSVladimir Kotal			debugon = True
372598cc7dfSVladimir Kotal		elif opt == '-i' :
37396ccc8cbSesaxe			fileNamesFile = val
37496ccc8cbSesaxe		elif opt == '-r' :
37596ccc8cbSesaxe			results = val
37696ccc8cbSesaxe			logging = True
377598cc7dfSVladimir Kotal		elif opt == '-s' :
378598cc7dfSVladimir Kotal			sorted = True
37996ccc8cbSesaxe		elif opt == '-v' :
38096ccc8cbSesaxe			vdiffs = True
38196ccc8cbSesaxe		elif opt == '-V' :
38296ccc8cbSesaxe			reportAllSects = True
38396ccc8cbSesaxe		elif opt == '-t':
38496ccc8cbSesaxe			localTools = True
38596ccc8cbSesaxe		else:
38696ccc8cbSesaxe			usage()
38796ccc8cbSesaxe
38896ccc8cbSesaxe	baseRoot = args[0]
38996ccc8cbSesaxe	ptchRoot = args[1]
39096ccc8cbSesaxe
39196ccc8cbSesaxe	if len(baseRoot) == 0 or len(ptchRoot) == 0 :
39296ccc8cbSesaxe		usage()
39396ccc8cbSesaxe
39496ccc8cbSesaxe	if logging and len(results) == 0 :
39596ccc8cbSesaxe		usage()
39696ccc8cbSesaxe
39796ccc8cbSesaxe	if vdiffs and not logging :
39896ccc8cbSesaxe		error("The -v option requires a results file (-r)")
39996ccc8cbSesaxe		sys.exit(1)
40096ccc8cbSesaxe
40196ccc8cbSesaxe	if reportAllSects and not logging :
40296ccc8cbSesaxe		error("The -V option requires a results file (-r)")
40396ccc8cbSesaxe		sys.exit(1)
40496ccc8cbSesaxe
40596ccc8cbSesaxe	# alphabetical order
40696ccc8cbSesaxe	return	baseRoot, fileNamesFile, localTools, ptchRoot, results
40796ccc8cbSesaxe
40896ccc8cbSesaxe#####
40996ccc8cbSesaxe# File identification
41096ccc8cbSesaxe#
41196ccc8cbSesaxe
41296ccc8cbSesaxe#
41396ccc8cbSesaxe# Identify the file type.
41496ccc8cbSesaxe# If it's not ELF, use the file extension to identify
41596ccc8cbSesaxe# certain file types that require special handling to
41696ccc8cbSesaxe# compare. Otherwise just return a basic "ASCII" type.
41796ccc8cbSesaxe#
41896ccc8cbSesaxedef getTheFileType(f) :
41996ccc8cbSesaxe
42096ccc8cbSesaxe	extensions = { 'a'	:	'ELF Object Archive',
42196ccc8cbSesaxe		       'jar'	:	'Java Archive',
42296ccc8cbSesaxe		       'html'	:	'HTML',
42396ccc8cbSesaxe		       'ln'	:	'Lint Library',
42496ccc8cbSesaxe		       'db'	:	'Sqlite Database' }
42596ccc8cbSesaxe
426619b4598Srotondo	try:
42796ccc8cbSesaxe		if os.stat(f)[ST_SIZE] == 0 :
42896ccc8cbSesaxe			return 'ASCII'
429619b4598Srotondo	except:
430619b4598Srotondo		error("failed to stat " + f)
431619b4598Srotondo		return 'Error'
43296ccc8cbSesaxe
43396ccc8cbSesaxe	if isELF(f) == 1 :
43496ccc8cbSesaxe		return 'ELF'
43596ccc8cbSesaxe
43696ccc8cbSesaxe	fnamelist = f.split('.')
43796ccc8cbSesaxe	if len(fnamelist) > 1 :	# Test the file extension
43896ccc8cbSesaxe		extension = fnamelist[-1]
43996ccc8cbSesaxe		if extension in extensions.keys():
44096ccc8cbSesaxe			return extensions[extension]
44196ccc8cbSesaxe
44296ccc8cbSesaxe	return 'ASCII'
44396ccc8cbSesaxe
44496ccc8cbSesaxe#
44596ccc8cbSesaxe# Return non-zero if "f" is an ELF file
44696ccc8cbSesaxe#
44796ccc8cbSesaxeelfmagic = '\177ELF'
44896ccc8cbSesaxedef isELF(f) :
44996ccc8cbSesaxe	try:
45096ccc8cbSesaxe		fd = open(f)
45196ccc8cbSesaxe	except:
45296ccc8cbSesaxe		error("failed to open: " + f)
45396ccc8cbSesaxe		return 0
45496ccc8cbSesaxe	magic = fd.read(len(elfmagic))
45596ccc8cbSesaxe	fd.close()
45696ccc8cbSesaxe
45796ccc8cbSesaxe	if magic == elfmagic :
45896ccc8cbSesaxe		return 1
45996ccc8cbSesaxe	return 0
46096ccc8cbSesaxe
46196ccc8cbSesaxe#
46296ccc8cbSesaxe# Return non-zero is "f" is binary.
46396ccc8cbSesaxe# Consider the file to be binary if it contains any null characters
46496ccc8cbSesaxe#
46596ccc8cbSesaxedef isBinary(f) :
46696ccc8cbSesaxe	try:
46796ccc8cbSesaxe		fd = open(f)
46896ccc8cbSesaxe	except:
46996ccc8cbSesaxe		error("failed to open: " + f)
47096ccc8cbSesaxe		return 0
47196ccc8cbSesaxe	s = fd.read()
47296ccc8cbSesaxe	fd.close()
47396ccc8cbSesaxe
47496ccc8cbSesaxe	if s.find('\0') == -1 :
47596ccc8cbSesaxe		return 0
47696ccc8cbSesaxe	else :
47796ccc8cbSesaxe		return 1
47896ccc8cbSesaxe
47996ccc8cbSesaxe#####
48096ccc8cbSesaxe# Directory traversal and file finding
48196ccc8cbSesaxe#
48296ccc8cbSesaxe
48396ccc8cbSesaxe#
48496ccc8cbSesaxe# Return a sorted list of files found under the specified directory
48596ccc8cbSesaxe#
48696ccc8cbSesaxedef findFiles(d) :
48796ccc8cbSesaxe	for path, subdirs, files in os.walk(d) :
48896ccc8cbSesaxe		files.sort()
48996ccc8cbSesaxe		for name in files :
49096ccc8cbSesaxe			yield os.path.join(path, name)
49196ccc8cbSesaxe
49296ccc8cbSesaxe#
49396ccc8cbSesaxe# Examine all files in base, ptch
49496ccc8cbSesaxe#
49596ccc8cbSesaxe# Return a list of files appearing in both proto areas,
49696ccc8cbSesaxe# a list of new files (files found only in ptch) and
49796ccc8cbSesaxe# a list of deleted files (files found only in base)
49896ccc8cbSesaxe#
49996ccc8cbSesaxedef protoCatalog(base, ptch) :
500598cc7dfSVladimir Kotal
50196ccc8cbSesaxe	compFiles = []		# List of files in both proto areas
50296ccc8cbSesaxe	ptchList = []		# List of file in patch proto area
50396ccc8cbSesaxe
50496ccc8cbSesaxe	newFiles = []		# New files detected
50596ccc8cbSesaxe	deletedFiles = []	# Deleted files
50696ccc8cbSesaxe
507598cc7dfSVladimir Kotal	debug("Getting the list of files in the base area");
50896ccc8cbSesaxe	baseFilesList = list(findFiles(base))
50996ccc8cbSesaxe	baseStringLength = len(base)
510598cc7dfSVladimir Kotal	debug("Found " + str(len(baseFilesList)) + " files")
51196ccc8cbSesaxe
512598cc7dfSVladimir Kotal	debug("Getting the list of files in the patch area");
51396ccc8cbSesaxe	ptchFilesList = list(findFiles(ptch))
51496ccc8cbSesaxe	ptchStringLength = len(ptch)
515598cc7dfSVladimir Kotal	debug("Found " + str(len(ptchFilesList)) + " files")
51696ccc8cbSesaxe
51796ccc8cbSesaxe	# Inventory files in the base proto area
518598cc7dfSVladimir Kotal	debug("Determining the list of regular files in the base area");
51996ccc8cbSesaxe	for fn in baseFilesList :
52096ccc8cbSesaxe		if os.path.islink(fn) :
52196ccc8cbSesaxe			continue
52296ccc8cbSesaxe
52396ccc8cbSesaxe		fileName = fn[baseStringLength:]
52496ccc8cbSesaxe		compFiles.append(fileName)
525598cc7dfSVladimir Kotal	debug("Found " + str(len(compFiles)) + " files")
52696ccc8cbSesaxe
52796ccc8cbSesaxe	# Inventory files in the patch proto area
528598cc7dfSVladimir Kotal	debug("Determining the list of regular files in the patch area");
52996ccc8cbSesaxe	for fn in ptchFilesList :
53096ccc8cbSesaxe		if os.path.islink(fn) :
53196ccc8cbSesaxe			continue
53296ccc8cbSesaxe
53396ccc8cbSesaxe		fileName = fn[ptchStringLength:]
53496ccc8cbSesaxe		ptchList.append(fileName)
535598cc7dfSVladimir Kotal	debug("Found " + str(len(ptchList)) + " files")
53696ccc8cbSesaxe
53796ccc8cbSesaxe	# Deleted files appear in the base area, but not the patch area
538598cc7dfSVladimir Kotal	debug("Searching for deleted files by comparing the lists")
53996ccc8cbSesaxe	for fileName in compFiles :
54096ccc8cbSesaxe		if not fileName in ptchList :
54196ccc8cbSesaxe			deletedFiles.append(fileName)
542598cc7dfSVladimir Kotal	debug("Found " + str(len(deletedFiles)) + " deleted files")
54396ccc8cbSesaxe
54496ccc8cbSesaxe	# Eliminate "deleted" files from the list of objects appearing
54596ccc8cbSesaxe	# in both the base and patch proto areas
546598cc7dfSVladimir Kotal	debug("Eliminating deleted files from the list of objects")
54796ccc8cbSesaxe	for fileName in deletedFiles :
54896ccc8cbSesaxe		try:
54996ccc8cbSesaxe		       	compFiles.remove(fileName)
55096ccc8cbSesaxe		except:
55196ccc8cbSesaxe			error("filelist.remove() failed")
552598cc7dfSVladimir Kotal	debug("List for comparison reduced to " + str(len(compFiles)) \
553598cc7dfSVladimir Kotal	    + " files")
55496ccc8cbSesaxe
55596ccc8cbSesaxe	# New files appear in the patch area, but not the base
556598cc7dfSVladimir Kotal	debug("Getting the list of newly added files")
55796ccc8cbSesaxe	for fileName in ptchList :
55896ccc8cbSesaxe		if not fileName in compFiles :
55996ccc8cbSesaxe			newFiles.append(fileName)
560598cc7dfSVladimir Kotal	debug("Found " + str(len(newFiles)) + " new files")
56196ccc8cbSesaxe
56296ccc8cbSesaxe	return compFiles, newFiles, deletedFiles
56396ccc8cbSesaxe
56496ccc8cbSesaxe#
56596ccc8cbSesaxe# Examine the files listed in the input file list
56696ccc8cbSesaxe#
56796ccc8cbSesaxe# Return a list of files appearing in both proto areas,
56896ccc8cbSesaxe# a list of new files (files found only in ptch) and
56996ccc8cbSesaxe# a list of deleted files (files found only in base)
57096ccc8cbSesaxe#
57196ccc8cbSesaxedef flistCatalog(base, ptch, flist) :
57296ccc8cbSesaxe	compFiles = []		# List of files in both proto areas
57396ccc8cbSesaxe	newFiles = []		# New files detected
57496ccc8cbSesaxe	deletedFiles = []	# Deleted files
57596ccc8cbSesaxe
57696ccc8cbSesaxe	try:
57796ccc8cbSesaxe		fd = open(flist, "r")
57896ccc8cbSesaxe	except:
57996ccc8cbSesaxe		error("could not open: " + flist)
58096ccc8cbSesaxe		cleanup(1)
58196ccc8cbSesaxe
58296ccc8cbSesaxe	files = []
58396ccc8cbSesaxe	files = fd.readlines()
584598cc7dfSVladimir Kotal	fd.close()
58596ccc8cbSesaxe
58696ccc8cbSesaxe	for f in files :
58796ccc8cbSesaxe		ptch_present = True
58896ccc8cbSesaxe		base_present = True
58996ccc8cbSesaxe
59096ccc8cbSesaxe		if f == '\n' :
59196ccc8cbSesaxe			continue
59296ccc8cbSesaxe
59396ccc8cbSesaxe		# the fileNames have a trailing '\n'
59496ccc8cbSesaxe		f = f.rstrip()
59596ccc8cbSesaxe
59696ccc8cbSesaxe		# The objects in the file list have paths relative
59796ccc8cbSesaxe		# to $ROOT or to the base/ptch directory specified on
59896ccc8cbSesaxe		# the command line.
59996ccc8cbSesaxe		# If it's relative to $ROOT, we'll need to add back the
60096ccc8cbSesaxe		# root_`uname -p` goo we stripped off in fnFormat()
60196ccc8cbSesaxe		if os.path.exists(base + f) :
60296ccc8cbSesaxe			fn = f;
60396ccc8cbSesaxe		elif os.path.exists(base + "root_" + arch + "/" + f) :
60496ccc8cbSesaxe			fn = "root_" + arch + "/" + f
60596ccc8cbSesaxe		else :
60696ccc8cbSesaxe			base_present = False
60796ccc8cbSesaxe
60896ccc8cbSesaxe		if base_present :
60996ccc8cbSesaxe			if not os.path.exists(ptch + fn) :
61096ccc8cbSesaxe				ptch_present = False
61196ccc8cbSesaxe		else :
61296ccc8cbSesaxe			if os.path.exists(ptch + f) :
61396ccc8cbSesaxe				fn = f
61496ccc8cbSesaxe			elif os.path.exists(ptch + "root_" + arch + "/" + f) :
61596ccc8cbSesaxe				fn = "root_" + arch + "/" + f
61696ccc8cbSesaxe			else :
61796ccc8cbSesaxe				ptch_present = False
61896ccc8cbSesaxe
61996ccc8cbSesaxe		if os.path.islink(base + fn) :	# ignore links
62096ccc8cbSesaxe			base_present = False
62196ccc8cbSesaxe		if os.path.islink(ptch + fn) :
62296ccc8cbSesaxe			ptch_present = False
62396ccc8cbSesaxe
62496ccc8cbSesaxe		if base_present and ptch_present :
62596ccc8cbSesaxe			compFiles.append(fn)
62696ccc8cbSesaxe		elif base_present :
62796ccc8cbSesaxe			deletedFiles.append(fn)
62896ccc8cbSesaxe		elif ptch_present :
62996ccc8cbSesaxe			newFiles.append(fn)
63096ccc8cbSesaxe		else :
631598cc7dfSVladimir Kotal			if os.path.islink(base + fn) and \
632598cc7dfSVladimir Kotal			    os.path.islink(ptch + fn) :
63396ccc8cbSesaxe				continue
634598cc7dfSVladimir Kotal			error(f + " in file list, but not in either tree. " + \
635598cc7dfSVladimir Kotal			    "Skipping...")
63696ccc8cbSesaxe
63796ccc8cbSesaxe	return compFiles, newFiles, deletedFiles
63896ccc8cbSesaxe
63996ccc8cbSesaxe
64096ccc8cbSesaxe#
64196ccc8cbSesaxe# Build a fully qualified path to an external tool/utility.
64296ccc8cbSesaxe# Consider the default system locations. For onbld tools, if
64396ccc8cbSesaxe# the -t option was specified, we'll try to use built tools in $SRC tools,
64496ccc8cbSesaxe# and otherwise, we'll fall back on /opt/onbld/
64596ccc8cbSesaxe#
64696ccc8cbSesaxedef find_tool(tool) :
64796ccc8cbSesaxe
64896ccc8cbSesaxe	# First, check what was passed
64996ccc8cbSesaxe	if os.path.exists(tool) :
65096ccc8cbSesaxe		return tool
65196ccc8cbSesaxe
65296ccc8cbSesaxe	# Next try in wsdiff path
65396ccc8cbSesaxe	for pdir in wsdiff_path :
65496ccc8cbSesaxe		location = pdir + "/" + tool
65596ccc8cbSesaxe		if os.path.exists(location) :
65696ccc8cbSesaxe			return location + " "
65796ccc8cbSesaxe
65896ccc8cbSesaxe		location = pdir + "/" + arch + "/" + tool
65996ccc8cbSesaxe		if os.path.exists(location) :
66096ccc8cbSesaxe			return location + " "
66196ccc8cbSesaxe
66296ccc8cbSesaxe	error("Could not find path to: " + tool);
66396ccc8cbSesaxe	sys.exit(1);
66496ccc8cbSesaxe
66596ccc8cbSesaxe
66696ccc8cbSesaxe#####
66796ccc8cbSesaxe# ELF file comparison helper routines
66896ccc8cbSesaxe#
66996ccc8cbSesaxe
67096ccc8cbSesaxe#
67196ccc8cbSesaxe# Return a dictionary of ELF section types keyed by section name
67296ccc8cbSesaxe#
67396ccc8cbSesaxedef get_elfheader(f) :
67496ccc8cbSesaxe
67596ccc8cbSesaxe	header = {}
67696ccc8cbSesaxe
67796ccc8cbSesaxe	hstring = commands.getoutput(elfdump_cmd + " -c " + f)
67896ccc8cbSesaxe
67996ccc8cbSesaxe	if len(hstring) == 0 :
68096ccc8cbSesaxe		error("Failed to dump ELF header for " + f)
681598cc7dfSVladimir Kotal		raise
68296ccc8cbSesaxe		return
68396ccc8cbSesaxe
68496ccc8cbSesaxe	# elfdump(1) dumps the section headers with the section name
68596ccc8cbSesaxe	# following "sh_name:", and the section type following "sh_type:"
68696ccc8cbSesaxe	sections = hstring.split("Section Header")
68796ccc8cbSesaxe	for sect in sections :
68896ccc8cbSesaxe		datap = sect.find("sh_name:");
68996ccc8cbSesaxe		if datap == -1 :
69096ccc8cbSesaxe			continue
69196ccc8cbSesaxe		section = sect[datap:].split()[1]
69296ccc8cbSesaxe		datap = sect.find("sh_type:");
69396ccc8cbSesaxe		if datap == -1 :
69496ccc8cbSesaxe			error("Could not get type for sect: " + section + \
69596ccc8cbSesaxe			      " in " + f)
69696ccc8cbSesaxe		sh_type = sect[datap:].split()[2]
69796ccc8cbSesaxe		header[section] = sh_type
69896ccc8cbSesaxe
69996ccc8cbSesaxe	return header
70096ccc8cbSesaxe
70196ccc8cbSesaxe#
70296ccc8cbSesaxe# Extract data in the specified ELF section from the given file
70396ccc8cbSesaxe#
70496ccc8cbSesaxedef extract_elf_section(f, section) :
70596ccc8cbSesaxe
70696ccc8cbSesaxe	data = commands.getoutput(dump_cmd + " -sn " + section + " " + f)
70796ccc8cbSesaxe
70896ccc8cbSesaxe	if len(data) == 0 :
709598cc7dfSVladimir Kotal		error(dump_cmd + "yielded no data on section " + section + \
710598cc7dfSVladimir Kotal		    " of " + f)
711598cc7dfSVladimir Kotal		raise
71296ccc8cbSesaxe		return
71396ccc8cbSesaxe
71496ccc8cbSesaxe	# dump(1) displays the file name to start...
71596ccc8cbSesaxe	# get past it to the data itself
71696ccc8cbSesaxe	dbegin = data.find(":") + 1
71796ccc8cbSesaxe	data = data[dbegin:];
71896ccc8cbSesaxe
71996ccc8cbSesaxe	return (data)
72096ccc8cbSesaxe
72196ccc8cbSesaxe#
72296ccc8cbSesaxe# Return a (hopefully meaningful) human readable set of diffs
72396ccc8cbSesaxe# for the specified ELF section between f1 and f2
72496ccc8cbSesaxe#
72596ccc8cbSesaxe# Depending on the section, various means for dumping and diffing
72696ccc8cbSesaxe# the data may be employed.
72796ccc8cbSesaxe#
72896ccc8cbSesaxetext_sections = [ '.text', '.init', '.fini' ]
72996ccc8cbSesaxedef diff_elf_section(f1, f2, section, sh_type) :
73096ccc8cbSesaxe
731598cc7dfSVladimir Kotal	t = threading.currentThread()
732598cc7dfSVladimir Kotal	tmpFile1 = tmpDir1 + os.path.basename(f1) + t.getName()
733598cc7dfSVladimir Kotal	tmpFile2 = tmpDir2 + os.path.basename(f2) + t.getName()
734598cc7dfSVladimir Kotal
73596ccc8cbSesaxe	if (sh_type == "SHT_RELA") : # sh_type == SHT_RELA
73696ccc8cbSesaxe		cmd1 = elfdump_cmd + " -r " + f1 + " > " + tmpFile1
73796ccc8cbSesaxe		cmd2 = elfdump_cmd + " -r " + f2 + " > " + tmpFile2
73896ccc8cbSesaxe	elif (section == ".group") :
73996ccc8cbSesaxe		cmd1 = elfdump_cmd + " -g " + f1 + " > " + tmpFile1
74096ccc8cbSesaxe		cmd2 = elfdump_cmd + " -g " + f2 + " > " + tmpFile2
74196ccc8cbSesaxe	elif (section == ".hash") :
74296ccc8cbSesaxe		cmd1 = elfdump_cmd + " -h " + f1 + " > " + tmpFile1
74396ccc8cbSesaxe		cmd2 = elfdump_cmd + " -h " + f2 + " > " + tmpFile2
74496ccc8cbSesaxe	elif (section == ".dynamic") :
74596ccc8cbSesaxe		cmd1 = elfdump_cmd + " -d " + f1 + " > " + tmpFile1
74696ccc8cbSesaxe		cmd2 = elfdump_cmd + " -d " + f2 + " > " + tmpFile2
74796ccc8cbSesaxe	elif (section == ".got") :
74896ccc8cbSesaxe		cmd1 = elfdump_cmd + " -G " + f1 + " > " + tmpFile1
74996ccc8cbSesaxe		cmd2 = elfdump_cmd + " -G " + f2 + " > " + tmpFile2
75096ccc8cbSesaxe	elif (section == ".SUNW_cap") :
75196ccc8cbSesaxe		cmd1 = elfdump_cmd + " -H " + f1 + " > " + tmpFile1
75296ccc8cbSesaxe		cmd2 = elfdump_cmd + " -H " + f2 + " > " + tmpFile2
75396ccc8cbSesaxe	elif (section == ".interp") :
75496ccc8cbSesaxe		cmd1 = elfdump_cmd + " -i " + f1 + " > " + tmpFile1
75596ccc8cbSesaxe		cmd2 = elfdump_cmd + " -i " + f2 + " > " + tmpFile2
75696ccc8cbSesaxe	elif (section == ".symtab" or section == ".dynsym") :
757598cc7dfSVladimir Kotal		cmd1 = elfdump_cmd + " -s -N " + section + " " + f1 + \
758598cc7dfSVladimir Kotal		    " > " + tmpFile1
759598cc7dfSVladimir Kotal		cmd2 = elfdump_cmd + " -s -N " + section + " " + f2 + \
760598cc7dfSVladimir Kotal		    " > " + tmpFile2
76196ccc8cbSesaxe	elif (section in text_sections) :
76296ccc8cbSesaxe		# dis sometimes complains when it hits something it doesn't
76396ccc8cbSesaxe		# know how to disassemble. Just ignore it, as the output
76496ccc8cbSesaxe		# being generated here is human readable, and we've already
76596ccc8cbSesaxe		# correctly flagged the difference.
76696ccc8cbSesaxe		cmd1 = dis_cmd + " -t " + section + " " + f1 + \
76796ccc8cbSesaxe		       " 2>/dev/null | grep -v disassembly > " + tmpFile1
76896ccc8cbSesaxe		cmd2 = dis_cmd + " -t " + section + " " + f2 + \
76996ccc8cbSesaxe		       " 2>/dev/null | grep -v disassembly > " + tmpFile2
77096ccc8cbSesaxe	else :
77196ccc8cbSesaxe		cmd1 = elfdump_cmd + " -w " + tmpFile1 + " -N " + \
77296ccc8cbSesaxe		       section + " " + f1
77396ccc8cbSesaxe		cmd2 = elfdump_cmd + " -w " + tmpFile2 + " -N " + \
77496ccc8cbSesaxe		       section + " " + f2
77596ccc8cbSesaxe
77696ccc8cbSesaxe	os.system(cmd1)
77796ccc8cbSesaxe	os.system(cmd2)
77896ccc8cbSesaxe
77996ccc8cbSesaxe	data = diffFileData(tmpFile1, tmpFile2)
78096ccc8cbSesaxe
781598cc7dfSVladimir Kotal	# remove temp files as we no longer need them
782598cc7dfSVladimir Kotal	try:
783598cc7dfSVladimir Kotal		os.unlink(tmpFile1)
784598cc7dfSVladimir Kotal	except OSError, e:
785598cc7dfSVladimir Kotal		error("diff_elf_section: unlink failed %s" % e)
786598cc7dfSVladimir Kotal	try:
787598cc7dfSVladimir Kotal		os.unlink(tmpFile2)
788598cc7dfSVladimir Kotal	except OSError, e:
789598cc7dfSVladimir Kotal		error("diff_elf_section: unlink failed %s" % e)
790598cc7dfSVladimir Kotal
79196ccc8cbSesaxe	return (data)
79296ccc8cbSesaxe
79396ccc8cbSesaxe#
79496ccc8cbSesaxe# compare the relevant sections of two ELF binaries
79596ccc8cbSesaxe# and report any differences
79696ccc8cbSesaxe#
79796ccc8cbSesaxe# Returns: 1 if any differenes found
79896ccc8cbSesaxe#          0 if no differences found
79996ccc8cbSesaxe#	  -1 on error
80096ccc8cbSesaxe#
80196ccc8cbSesaxe
80296ccc8cbSesaxe# Sections deliberately not considered when comparing two ELF
80396ccc8cbSesaxe# binaries. Differences observed in these sections are not considered
80496ccc8cbSesaxe# significant where patch deliverable identification is concerned.
80596ccc8cbSesaxesections_to_skip = [ ".SUNW_signature",
80696ccc8cbSesaxe		     ".comment",
80796ccc8cbSesaxe		     ".SUNW_ctf",
80896ccc8cbSesaxe		     ".debug",
80996ccc8cbSesaxe		     ".plt",
81096ccc8cbSesaxe		     ".rela.bss",
81196ccc8cbSesaxe		     ".rela.plt",
81296ccc8cbSesaxe		     ".line",
81396ccc8cbSesaxe		     ".note",
814f6a1d796Sesaxe		     ".compcom",
81596ccc8cbSesaxe		     ]
81696ccc8cbSesaxe
81796ccc8cbSesaxesections_preferred = [ ".rodata.str1.8",
81896ccc8cbSesaxe		       ".rodata.str1.1",
81996ccc8cbSesaxe		       ".rodata",
82096ccc8cbSesaxe		       ".data1",
82196ccc8cbSesaxe		       ".data",
82296ccc8cbSesaxe		       ".text",
82396ccc8cbSesaxe		       ]
82496ccc8cbSesaxe
82596ccc8cbSesaxedef compareElfs(base, ptch, quiet) :
82696ccc8cbSesaxe
82796ccc8cbSesaxe	global logging
82896ccc8cbSesaxe
829598cc7dfSVladimir Kotal	try:
83096ccc8cbSesaxe		base_header = get_elfheader(base)
831598cc7dfSVladimir Kotal	except:
832598cc7dfSVladimir Kotal		return
83396ccc8cbSesaxe 	sections = base_header.keys()
83496ccc8cbSesaxe
835598cc7dfSVladimir Kotal	try:
83696ccc8cbSesaxe		ptch_header = get_elfheader(ptch)
837598cc7dfSVladimir Kotal	except:
838598cc7dfSVladimir Kotal		return
83996ccc8cbSesaxe	e2_only_sections = ptch_header.keys()
84096ccc8cbSesaxe
84196ccc8cbSesaxe	e1_only_sections = []
84296ccc8cbSesaxe
84396ccc8cbSesaxe	fileName = fnFormat(base)
84496ccc8cbSesaxe
84596ccc8cbSesaxe	# Derive the list of ELF sections found only in
84696ccc8cbSesaxe	# either e1 or e2.
84796ccc8cbSesaxe	for sect in sections :
84896ccc8cbSesaxe		if not sect in e2_only_sections :
84996ccc8cbSesaxe			e1_only_sections.append(sect)
85096ccc8cbSesaxe		else :
85196ccc8cbSesaxe			e2_only_sections.remove(sect)
85296ccc8cbSesaxe
85396ccc8cbSesaxe	if len(e1_only_sections) > 0 :
85496ccc8cbSesaxe		if quiet :
85596ccc8cbSesaxe			return 1
85696ccc8cbSesaxe
857598cc7dfSVladimir Kotal		data = ""
858598cc7dfSVladimir Kotal		if logging :
85996ccc8cbSesaxe			slist = ""
86096ccc8cbSesaxe			for sect in e1_only_sections :
86196ccc8cbSesaxe				slist = slist + sect + "\t"
862598cc7dfSVladimir Kotal			data = "ELF sections found in " + \
863598cc7dfSVladimir Kotal				base + " but not in " + ptch + \
864598cc7dfSVladimir Kotal				"\n\n" + slist
865598cc7dfSVladimir Kotal
866598cc7dfSVladimir Kotal		difference(fileName, "ELF", data)
86796ccc8cbSesaxe		return 1
86896ccc8cbSesaxe
86996ccc8cbSesaxe	if len(e2_only_sections) > 0 :
87096ccc8cbSesaxe		if quiet :
87196ccc8cbSesaxe			return 1
87296ccc8cbSesaxe
873598cc7dfSVladimir Kotal		data = ""
874598cc7dfSVladimir Kotal		if logging :
87596ccc8cbSesaxe			slist = ""
87696ccc8cbSesaxe			for sect in e2_only_sections :
87796ccc8cbSesaxe				slist = slist + sect + "\t"
878598cc7dfSVladimir Kotal			data = "ELF sections found in " + \
879598cc7dfSVladimir Kotal				ptch + " but not in " + base + \
880598cc7dfSVladimir Kotal				"\n\n" + slist
881598cc7dfSVladimir Kotal
882598cc7dfSVladimir Kotal		difference(fileName, "ELF", data)
88396ccc8cbSesaxe		return 1
88496ccc8cbSesaxe
88596ccc8cbSesaxe	# Look for preferred sections, and put those at the
88696ccc8cbSesaxe	# top of the list of sections to compare
88796ccc8cbSesaxe	for psect in sections_preferred :
88896ccc8cbSesaxe		if psect in sections :
88996ccc8cbSesaxe			sections.remove(psect)
89096ccc8cbSesaxe			sections.insert(0, psect)
89196ccc8cbSesaxe
89296ccc8cbSesaxe	# Compare ELF sections
89396ccc8cbSesaxe	first_section = True
89496ccc8cbSesaxe	for sect in sections :
89596ccc8cbSesaxe
89696ccc8cbSesaxe		if sect in sections_to_skip :
89796ccc8cbSesaxe			continue
89896ccc8cbSesaxe
899598cc7dfSVladimir Kotal		try:
90096ccc8cbSesaxe			s1 = extract_elf_section(base, sect);
901598cc7dfSVladimir Kotal		except:
902598cc7dfSVladimir Kotal			return
903598cc7dfSVladimir Kotal
904598cc7dfSVladimir Kotal		try:
90596ccc8cbSesaxe			s2 = extract_elf_section(ptch, sect);
906598cc7dfSVladimir Kotal		except:
907598cc7dfSVladimir Kotal			return
90896ccc8cbSesaxe
90996ccc8cbSesaxe		if len(s1) != len (s2) or s1 != s2:
91096ccc8cbSesaxe			if not quiet:
91196ccc8cbSesaxe				sh_type = base_header[sect]
912598cc7dfSVladimir Kotal				data = diff_elf_section(base, ptch, \
913598cc7dfSVladimir Kotal							sect, sh_type)
91496ccc8cbSesaxe
91596ccc8cbSesaxe				# If all ELF sections are being reported, then
91696ccc8cbSesaxe				# invoke difference() to flag the file name to
91796ccc8cbSesaxe				# stdout only once. Any other section differences
91896ccc8cbSesaxe				# should be logged to the results file directly
91996ccc8cbSesaxe				if not first_section :
920598cc7dfSVladimir Kotal					log_difference(fileName, \
921598cc7dfSVladimir Kotal					    "ELF " + sect, data)
92296ccc8cbSesaxe				else :
923598cc7dfSVladimir Kotal					difference(fileName, "ELF " + sect, \
924598cc7dfSVladimir Kotal					    data)
92596ccc8cbSesaxe
92696ccc8cbSesaxe			if not reportAllSects :
92796ccc8cbSesaxe				return 1
92896ccc8cbSesaxe			first_section = False
929598cc7dfSVladimir Kotal
93096ccc8cbSesaxe	return 0
93196ccc8cbSesaxe
93296ccc8cbSesaxe#####
933598cc7dfSVladimir Kotal# recursively remove 2 directories
934598cc7dfSVladimir Kotal#
935598cc7dfSVladimir Kotal# Used for removal of temporary directory strucures (ignores any errors).
936598cc7dfSVladimir Kotal#
937598cc7dfSVladimir Kotaldef clearTmpDirs(dir1, dir2) :
938598cc7dfSVladimir Kotal
939598cc7dfSVladimir Kotal	if os.path.isdir(dir1) > 0 :
940598cc7dfSVladimir Kotal		shutil.rmtree(dir1, True)
941598cc7dfSVladimir Kotal
942598cc7dfSVladimir Kotal	if os.path.isdir(dir2) > 0 :
943598cc7dfSVladimir Kotal		shutil.rmtree(dir2, True)
944598cc7dfSVladimir Kotal
945598cc7dfSVladimir Kotal
946598cc7dfSVladimir Kotal#####
94796ccc8cbSesaxe# Archive object comparison
94896ccc8cbSesaxe#
94996ccc8cbSesaxe# Returns 1 if difference detected
95096ccc8cbSesaxe#         0 if no difference detected
95196ccc8cbSesaxe#        -1 on error
95296ccc8cbSesaxe#
95396ccc8cbSesaxedef compareArchives(base, ptch, fileType) :
95496ccc8cbSesaxe
95596ccc8cbSesaxe	fileName = fnFormat(base)
956598cc7dfSVladimir Kotal	t = threading.currentThread()
957598cc7dfSVladimir Kotal	ArchTmpDir1 = tmpDir1 + os.path.basename(base) + t.getName()
958598cc7dfSVladimir Kotal	ArchTmpDir2 = tmpDir2 + os.path.basename(base) + t.getName()
95996ccc8cbSesaxe
96096ccc8cbSesaxe	#
96196ccc8cbSesaxe	# Be optimistic and first try a straight file compare
96296ccc8cbSesaxe	# as it will allow us to finish up quickly.
963598cc7dfSVladimir Kotal	#
96496ccc8cbSesaxe	if compareBasic(base, ptch, True, fileType) == 0 :
96596ccc8cbSesaxe		return 0
96696ccc8cbSesaxe
967598cc7dfSVladimir Kotal	try:
968598cc7dfSVladimir Kotal		os.makedirs(ArchTmpDir1)
969598cc7dfSVladimir Kotal	except OSError, e:
970598cc7dfSVladimir Kotal		error("compareArchives: makedir failed %s" % e)
971598cc7dfSVladimir Kotal		return -1
972598cc7dfSVladimir Kotal	try:
973598cc7dfSVladimir Kotal		os.makedirs(ArchTmpDir2)
974598cc7dfSVladimir Kotal	except OSError, e:
975598cc7dfSVladimir Kotal		error("compareArchives: makedir failed %s" % e)
976598cc7dfSVladimir Kotal		return -1
977598cc7dfSVladimir Kotal
97896ccc8cbSesaxe	# copy over the objects to the temp areas, and
97996ccc8cbSesaxe	# unpack them
980598cc7dfSVladimir Kotal	baseCmd = "cp -fp " + base + " " + ArchTmpDir1
98196ccc8cbSesaxe	status, output = commands.getstatusoutput(baseCmd)
98296ccc8cbSesaxe	if status != 0 :
98396ccc8cbSesaxe		error(baseCmd + " failed: " + output)
984598cc7dfSVladimir Kotal		clearTmpDirs(ArchTmpDir1, ArchTmpDir2)
98596ccc8cbSesaxe		return -1
98696ccc8cbSesaxe
987598cc7dfSVladimir Kotal	ptchCmd = "cp -fp " + ptch + " " + ArchTmpDir2
98896ccc8cbSesaxe	status, output = commands.getstatusoutput(ptchCmd)
98996ccc8cbSesaxe	if status != 0 :
99096ccc8cbSesaxe		error(ptchCmd + " failed: " + output)
991598cc7dfSVladimir Kotal		clearTmpDirs(ArchTmpDir1, ArchTmpDir2)
99296ccc8cbSesaxe		return -1
99396ccc8cbSesaxe
99496ccc8cbSesaxe	bname = string.split(fileName, '/')[-1]
99596ccc8cbSesaxe	if fileType == "Java Archive" :
996598cc7dfSVladimir Kotal		baseCmd = "cd " + ArchTmpDir1 + "; " + "jar xf " + bname + \
99796ccc8cbSesaxe			  "; rm -f " + bname + " META-INF/MANIFEST.MF"
998598cc7dfSVladimir Kotal		ptchCmd = "cd " + ArchTmpDir2 + "; " + "jar xf " + bname + \
99996ccc8cbSesaxe			  "; rm -f " + bname + " META-INF/MANIFEST.MF"
100096ccc8cbSesaxe	elif fileType == "ELF Object Archive" :
1001598cc7dfSVladimir Kotal		baseCmd = "cd " + ArchTmpDir1 + "; " + "/usr/ccs/bin/ar x " + \
100296ccc8cbSesaxe			  bname + "; rm -f " + bname
1003598cc7dfSVladimir Kotal		ptchCmd = "cd " + ArchTmpDir2 + "; " + "/usr/ccs/bin/ar x " + \
100496ccc8cbSesaxe			  bname + "; rm -f " + bname
100596ccc8cbSesaxe	else :
100696ccc8cbSesaxe		error("unexpected file type: " + fileType)
1007598cc7dfSVladimir Kotal		clearTmpDirs(ArchTmpDir1, ArchTmpDir2)
100896ccc8cbSesaxe		return -1
100996ccc8cbSesaxe
101096ccc8cbSesaxe	os.system(baseCmd)
101196ccc8cbSesaxe	os.system(ptchCmd)
101296ccc8cbSesaxe
1013598cc7dfSVladimir Kotal	baseFlist = list(findFiles(ArchTmpDir1))
1014598cc7dfSVladimir Kotal	ptchFlist = list(findFiles(ArchTmpDir2))
101596ccc8cbSesaxe
101696ccc8cbSesaxe	# Trim leading path off base/ptch file lists
101796ccc8cbSesaxe	flist = []
101896ccc8cbSesaxe	for fn in baseFlist :
1019598cc7dfSVladimir Kotal		flist.append(str_prefix_trunc(fn, ArchTmpDir1))
102096ccc8cbSesaxe	baseFlist = flist
102196ccc8cbSesaxe
102296ccc8cbSesaxe	flist = []
102396ccc8cbSesaxe	for fn in ptchFlist :
1024598cc7dfSVladimir Kotal		flist.append(str_prefix_trunc(fn, ArchTmpDir2))
102596ccc8cbSesaxe	ptchFlist = flist
102696ccc8cbSesaxe
102796ccc8cbSesaxe	for fn in ptchFlist :
102896ccc8cbSesaxe		if not fn in baseFlist :
102996ccc8cbSesaxe			difference(fileName, fileType, \
103096ccc8cbSesaxe				   fn + " added to " + fileName)
1031598cc7dfSVladimir Kotal			clearTmpDirs(ArchTmpDir1, ArchTmpDir2)
103296ccc8cbSesaxe			return 1
103396ccc8cbSesaxe
103496ccc8cbSesaxe	for fn in baseFlist :
103596ccc8cbSesaxe		if not fn in ptchFlist :
103696ccc8cbSesaxe			difference(fileName, fileType, \
103796ccc8cbSesaxe				   fn + " removed from " + fileName)
1038598cc7dfSVladimir Kotal			clearTmpDirs(ArchTmpDir1, ArchTmpDir2)
103996ccc8cbSesaxe			return 1
104096ccc8cbSesaxe
1041598cc7dfSVladimir Kotal		differs = compareOneFile((ArchTmpDir1 + fn), \
1042598cc7dfSVladimir Kotal		    (ArchTmpDir2 + fn), True)
104396ccc8cbSesaxe		if differs :
104496ccc8cbSesaxe			difference(fileName, fileType, \
104596ccc8cbSesaxe				   fn + " in " + fileName + " differs")
1046598cc7dfSVladimir Kotal			clearTmpDirs(ArchTmpDir1, ArchTmpDir2)
104796ccc8cbSesaxe			return 1
1048598cc7dfSVladimir Kotal
1049598cc7dfSVladimir Kotal	clearTmpDirs(ArchTmpDir1, ArchTmpDir2)
105096ccc8cbSesaxe	return 0
105196ccc8cbSesaxe
105296ccc8cbSesaxe#####
105396ccc8cbSesaxe# (Basic) file comparison
105496ccc8cbSesaxe#
105596ccc8cbSesaxe# There's some special case code here for Javadoc HTML files
105696ccc8cbSesaxe#
105796ccc8cbSesaxe# Returns 1 if difference detected
105896ccc8cbSesaxe#         0 if no difference detected
105996ccc8cbSesaxe#        -1 on error
106096ccc8cbSesaxe#
106196ccc8cbSesaxedef compareBasic(base, ptch, quiet, fileType) :
106296ccc8cbSesaxe
106396ccc8cbSesaxe	fileName = fnFormat(base);
106496ccc8cbSesaxe
106596ccc8cbSesaxe	if quiet and os.stat(base)[ST_SIZE] != os.stat(ptch)[ST_SIZE] :
106696ccc8cbSesaxe		return 1
106796ccc8cbSesaxe
106896ccc8cbSesaxe	try:
106996ccc8cbSesaxe		baseFile = open(base)
107096ccc8cbSesaxe	except:
107196ccc8cbSesaxe		error("could not open " + base)
107296ccc8cbSesaxe		return -1
107396ccc8cbSesaxe	try:
107496ccc8cbSesaxe		ptchFile = open(ptch)
107596ccc8cbSesaxe	except:
107696ccc8cbSesaxe		error("could not open " + ptch)
107796ccc8cbSesaxe		return -1
107896ccc8cbSesaxe
107996ccc8cbSesaxe	baseData = baseFile.read()
108096ccc8cbSesaxe	ptchData = ptchFile.read()
108196ccc8cbSesaxe
108296ccc8cbSesaxe	baseFile.close()
108396ccc8cbSesaxe	ptchFile.close()
108496ccc8cbSesaxe
108596ccc8cbSesaxe	needToSnip = False
108696ccc8cbSesaxe	if fileType == "HTML" :
108796ccc8cbSesaxe		needToSnip = True
108896ccc8cbSesaxe		toSnipBeginStr = "<!-- Generated by javadoc"
108996ccc8cbSesaxe		toSnipEndStr = "-->\n"
109096ccc8cbSesaxe
109196ccc8cbSesaxe	if needToSnip :
109296ccc8cbSesaxe		toSnipBegin = string.find(baseData, toSnipBeginStr)
109396ccc8cbSesaxe		if toSnipBegin != -1 :
109496ccc8cbSesaxe			toSnipEnd = string.find(baseData[toSnipBegin:], \
109596ccc8cbSesaxe						toSnipEndStr) + \
109696ccc8cbSesaxe						len(toSnipEndStr)
109796ccc8cbSesaxe			baseData = baseData[:toSnipBegin] + \
109896ccc8cbSesaxe				   baseData[toSnipBegin + toSnipEnd:]
109996ccc8cbSesaxe			ptchData = ptchData[:toSnipBegin] + \
110096ccc8cbSesaxe				   ptchData[toSnipBegin + toSnipEnd:]
110196ccc8cbSesaxe
110296ccc8cbSesaxe	if quiet :
110396ccc8cbSesaxe		if baseData != ptchData :
110496ccc8cbSesaxe			return 1
110596ccc8cbSesaxe	else :
110696ccc8cbSesaxe		if len(baseData) != len(ptchData) or baseData != ptchData :
1107598cc7dfSVladimir Kotal			diffs = diffData(base, ptch, baseData, ptchData)
110896ccc8cbSesaxe			difference(fileName, fileType, diffs)
110996ccc8cbSesaxe			return 1
111096ccc8cbSesaxe	return 0
111196ccc8cbSesaxe
111296ccc8cbSesaxe
111396ccc8cbSesaxe#####
111496ccc8cbSesaxe# Compare two objects by producing a data dump from
111596ccc8cbSesaxe# each object, and then comparing the dump data
111696ccc8cbSesaxe#
111796ccc8cbSesaxe# Returns: 1 if a difference is detected
111896ccc8cbSesaxe#          0 if no difference detected
111996ccc8cbSesaxe#         -1 upon error
112096ccc8cbSesaxe#
112196ccc8cbSesaxedef compareByDumping(base, ptch, quiet, fileType) :
112296ccc8cbSesaxe
112396ccc8cbSesaxe	fileName = fnFormat(base);
1124598cc7dfSVladimir Kotal	t = threading.currentThread()
1125598cc7dfSVladimir Kotal	tmpFile1 = tmpDir1 + os.path.basename(base) + t.getName()
1126598cc7dfSVladimir Kotal	tmpFile2 = tmpDir2 + os.path.basename(ptch) + t.getName()
112796ccc8cbSesaxe
112896ccc8cbSesaxe	if fileType == "Lint Library" :
112996ccc8cbSesaxe		baseCmd = lintdump_cmd + " -ir " + base + \
1130598cc7dfSVladimir Kotal			  " | egrep -v '(LINTOBJ|LINTMOD):'" + \
1131598cc7dfSVladimir Kotal			  " | grep -v PASS[1-3]:" + \
1132598cc7dfSVladimir Kotal			  " > " + tmpFile1
113396ccc8cbSesaxe		ptchCmd = lintdump_cmd + " -ir " + ptch + \
1134598cc7dfSVladimir Kotal			  " | egrep -v '(LINTOBJ|LINTMOD):'" + \
1135598cc7dfSVladimir Kotal			  " | grep -v PASS[1-3]:" + \
1136598cc7dfSVladimir Kotal			  " > " + tmpFile2
113796ccc8cbSesaxe	elif fileType == "Sqlite Database" :
113896ccc8cbSesaxe		baseCmd = "echo .dump | " + sqlite_cmd + base + " > " + \
113996ccc8cbSesaxe			  tmpFile1
114096ccc8cbSesaxe		ptchCmd = "echo .dump | " + sqlite_cmd + ptch + " > " + \
114196ccc8cbSesaxe			  tmpFile2
114296ccc8cbSesaxe
114396ccc8cbSesaxe	os.system(baseCmd)
114496ccc8cbSesaxe	os.system(ptchCmd)
114596ccc8cbSesaxe
114696ccc8cbSesaxe	try:
114796ccc8cbSesaxe		baseFile = open(tmpFile1)
114896ccc8cbSesaxe	except:
114996ccc8cbSesaxe		error("could not open: " + tmpFile1)
1150598cc7dfSVladimir Kotal		return
115196ccc8cbSesaxe	try:
115296ccc8cbSesaxe		ptchFile = open(tmpFile2)
115396ccc8cbSesaxe	except:
115496ccc8cbSesaxe		error("could not open: " + tmpFile2)
1155598cc7dfSVladimir Kotal		return
115696ccc8cbSesaxe
115796ccc8cbSesaxe	baseData = baseFile.read()
115896ccc8cbSesaxe	ptchData = ptchFile.read()
115996ccc8cbSesaxe
116096ccc8cbSesaxe	baseFile.close()
116196ccc8cbSesaxe	ptchFile.close()
116296ccc8cbSesaxe
116396ccc8cbSesaxe	if len(baseData) != len(ptchData) or baseData != ptchData :
116496ccc8cbSesaxe		if not quiet :
116596ccc8cbSesaxe			data = diffFileData(tmpFile1, tmpFile2);
1166598cc7dfSVladimir Kotal			try:
1167598cc7dfSVladimir Kotal				os.unlink(tmpFile1)
1168598cc7dfSVladimir Kotal			except OSError, e:
1169598cc7dfSVladimir Kotal				error("compareByDumping: unlink failed %s" % e)
1170598cc7dfSVladimir Kotal			try:
1171598cc7dfSVladimir Kotal				os.unlink(tmpFile2)
1172598cc7dfSVladimir Kotal			except OSError, e:
1173598cc7dfSVladimir Kotal				error("compareByDumping: unlink failed %s" % e)
117496ccc8cbSesaxe			difference(fileName, fileType, data)
117596ccc8cbSesaxe 		return 1
1176598cc7dfSVladimir Kotal
1177598cc7dfSVladimir Kotal	# Remove the temporary files now.
1178598cc7dfSVladimir Kotal	try:
1179598cc7dfSVladimir Kotal		os.unlink(tmpFile1)
1180598cc7dfSVladimir Kotal	except OSError, e:
1181598cc7dfSVladimir Kotal		error("compareByDumping: unlink failed %s" % e)
1182598cc7dfSVladimir Kotal	try:
1183598cc7dfSVladimir Kotal		os.unlink(tmpFile2)
1184598cc7dfSVladimir Kotal	except OSError, e:
1185598cc7dfSVladimir Kotal		error("compareByDumping: unlink failed %s" % e)
1186598cc7dfSVladimir Kotal
118796ccc8cbSesaxe	return 0
118896ccc8cbSesaxe
118996ccc8cbSesaxe#####
1190619b4598Srotondo#
1191598cc7dfSVladimir Kotal# SIGINT signal handler. Changes thread control variable to tell the threads
1192598cc7dfSVladimir Kotal# to finish their current job and exit.
1193619b4598Srotondo#
1194598cc7dfSVladimir Kotaldef discontinue_processing(signl, frme):
1195598cc7dfSVladimir Kotal	global keep_processing
1196619b4598Srotondo
1197598cc7dfSVladimir Kotal	print >> sys.stderr, "Caught Ctrl-C, stopping the threads"
1198598cc7dfSVladimir Kotal	keep_processing = False
1199619b4598Srotondo
1200598cc7dfSVladimir Kotal	return 0
1201619b4598Srotondo
1202598cc7dfSVladimir Kotal#####
1203598cc7dfSVladimir Kotal#
1204598cc7dfSVladimir Kotal# worker thread for changedFiles processing
1205598cc7dfSVladimir Kotal#
1206598cc7dfSVladimir Kotalclass workerThread(threading.Thread) :
1207598cc7dfSVladimir Kotal    def run(self):
1208598cc7dfSVladimir Kotal	global wset_lock
1209598cc7dfSVladimir Kotal	global changedFiles
1210598cc7dfSVladimir Kotal	global baseRoot
1211598cc7dfSVladimir Kotal	global ptchRoot
1212598cc7dfSVladimir Kotal	global keep_processing
1213619b4598Srotondo
1214598cc7dfSVladimir Kotal	while (keep_processing) :
1215598cc7dfSVladimir Kotal		# grab the lock to changedFiles and remove one member
1216598cc7dfSVladimir Kotal		# and process it
1217598cc7dfSVladimir Kotal		wset_lock.acquire()
1218598cc7dfSVladimir Kotal		try :
1219598cc7dfSVladimir Kotal			fn = changedFiles.pop()
1220598cc7dfSVladimir Kotal		except IndexError :
1221598cc7dfSVladimir Kotal			# there is nothing more to do
1222598cc7dfSVladimir Kotal			wset_lock.release()
1223598cc7dfSVladimir Kotal			return
1224598cc7dfSVladimir Kotal		wset_lock.release()
1225598cc7dfSVladimir Kotal
1226598cc7dfSVladimir Kotal		base = baseRoot + fn
1227598cc7dfSVladimir Kotal		ptch = ptchRoot + fn
1228598cc7dfSVladimir Kotal
1229598cc7dfSVladimir Kotal		compareOneFile(base, ptch, False)
1230598cc7dfSVladimir Kotal
1231619b4598Srotondo
1232619b4598Srotondo#####
123396ccc8cbSesaxe# Compare two objects. Detect type changes.
123496ccc8cbSesaxe# Vector off to the appropriate type specific
123596ccc8cbSesaxe# compare routine based on the type.
123696ccc8cbSesaxe#
123796ccc8cbSesaxedef compareOneFile(base, ptch, quiet) :
123896ccc8cbSesaxe
123996ccc8cbSesaxe	# Verify the file types.
124096ccc8cbSesaxe	# If they are different, indicate this and move on
124196ccc8cbSesaxe	btype = getTheFileType(base)
124296ccc8cbSesaxe	ptype = getTheFileType(ptch)
124396ccc8cbSesaxe
1244619b4598Srotondo	if btype == 'Error' or ptype == 'Error' :
1245619b4598Srotondo		return -1
1246619b4598Srotondo
124796ccc8cbSesaxe	fileName = fnFormat(base)
124896ccc8cbSesaxe
124996ccc8cbSesaxe	if (btype != ptype) :
1250619b4598Srotondo		if not quiet :
125196ccc8cbSesaxe			difference(fileName, "file type", btype + " to " + ptype)
125296ccc8cbSesaxe		return 1
125396ccc8cbSesaxe	else :
125496ccc8cbSesaxe		fileType = btype
125596ccc8cbSesaxe
125696ccc8cbSesaxe	if (fileType == 'ELF') :
125796ccc8cbSesaxe		return compareElfs(base, ptch, quiet)
125896ccc8cbSesaxe
125996ccc8cbSesaxe	elif (fileType == 'Java Archive' or fileType == 'ELF Object Archive') :
126096ccc8cbSesaxe		return compareArchives(base, ptch, fileType)
126196ccc8cbSesaxe
126296ccc8cbSesaxe	elif (fileType == 'HTML') :
126396ccc8cbSesaxe		return compareBasic(base, ptch, quiet, fileType)
126496ccc8cbSesaxe
126596ccc8cbSesaxe	elif ( fileType == 'Lint Library' ) :
126696ccc8cbSesaxe		return compareByDumping(base, ptch, quiet, fileType)
126796ccc8cbSesaxe
126896ccc8cbSesaxe	elif ( fileType == 'Sqlite Database' ) :
126996ccc8cbSesaxe		return compareByDumping(base, ptch, quiet, fileType)
1270619b4598Srotondo
127196ccc8cbSesaxe	else :
127296ccc8cbSesaxe		# it has to be some variety of text file
127396ccc8cbSesaxe		return compareBasic(base, ptch, quiet, fileType)
127496ccc8cbSesaxe
127596ccc8cbSesaxe# Cleanup and self-terminate
127696ccc8cbSesaxedef cleanup(ret) :
127796ccc8cbSesaxe
1278598cc7dfSVladimir Kotal	debug("Performing cleanup (" + str(ret) + ")")
1279598cc7dfSVladimir Kotal	if os.path.isdir(tmpDir1) > 0 :
1280598cc7dfSVladimir Kotal		shutil.rmtree(tmpDir1)
128196ccc8cbSesaxe
1282598cc7dfSVladimir Kotal	if os.path.isdir(tmpDir2) > 0 :
1283598cc7dfSVladimir Kotal		shutil.rmtree(tmpDir2)
128496ccc8cbSesaxe
128596ccc8cbSesaxe	if logging :
128696ccc8cbSesaxe		log.close()
128796ccc8cbSesaxe
128896ccc8cbSesaxe	sys.exit(ret)
128996ccc8cbSesaxe
129096ccc8cbSesaxedef main() :
129196ccc8cbSesaxe
129296ccc8cbSesaxe	# Log file handle
129396ccc8cbSesaxe	global log
129496ccc8cbSesaxe
129596ccc8cbSesaxe	# Globals relating to command line options
129696ccc8cbSesaxe	global logging, vdiffs, reportAllSects
129796ccc8cbSesaxe
129896ccc8cbSesaxe	# Named temporary files / directories
1299598cc7dfSVladimir Kotal	global tmpDir1, tmpDir2
130096ccc8cbSesaxe
130196ccc8cbSesaxe	# Command paths
130296ccc8cbSesaxe	global lintdump_cmd, elfdump_cmd, dump_cmd, dis_cmd, od_cmd, diff_cmd, sqlite_cmd
130396ccc8cbSesaxe
130496ccc8cbSesaxe	# Default search path
130596ccc8cbSesaxe	global wsdiff_path
130696ccc8cbSesaxe
130796ccc8cbSesaxe	# Essentially "uname -p"
130896ccc8cbSesaxe	global arch
130996ccc8cbSesaxe
1310598cc7dfSVladimir Kotal	# changed files for worker thread processing
1311598cc7dfSVladimir Kotal	global changedFiles
1312598cc7dfSVladimir Kotal	global baseRoot
1313598cc7dfSVladimir Kotal	global ptchRoot
1314598cc7dfSVladimir Kotal
1315598cc7dfSVladimir Kotal	# Sort the list of files from a temporary file
1316598cc7dfSVladimir Kotal	global sorted
1317598cc7dfSVladimir Kotal	global differentFiles
1318598cc7dfSVladimir Kotal
1319598cc7dfSVladimir Kotal	# Debugging indicator
1320598cc7dfSVladimir Kotal	global debugon
1321598cc7dfSVladimir Kotal
132296ccc8cbSesaxe	# Some globals need to be initialized
1323598cc7dfSVladimir Kotal	debugon = logging = vdiffs = reportAllSects = sorted = False
132496ccc8cbSesaxe
132596ccc8cbSesaxe
132696ccc8cbSesaxe	# Process command line arguments
132796ccc8cbSesaxe	# Return values are returned from args() in alpha order
132896ccc8cbSesaxe	# (Yes, python functions can return multiple values (ewww))
132996ccc8cbSesaxe	# Note that args() also set the globals:
133096ccc8cbSesaxe	#	logging to True if verbose logging (to a file) was enabled
133196ccc8cbSesaxe	#	vdiffs to True if logged differences aren't to be truncated
133296ccc8cbSesaxe	#	reportAllSects to True if all ELF section differences are to be reported
133396ccc8cbSesaxe	#
133496ccc8cbSesaxe	baseRoot, fileNamesFile, localTools, ptchRoot, results = args()
133596ccc8cbSesaxe
133696ccc8cbSesaxe	#
133796ccc8cbSesaxe	# Set up the results/log file
133896ccc8cbSesaxe	#
133996ccc8cbSesaxe	if logging :
134096ccc8cbSesaxe		try:
134196ccc8cbSesaxe			log = open(results, "w")
134296ccc8cbSesaxe		except:
134396ccc8cbSesaxe			logging = False
134496ccc8cbSesaxe			error("failed to open log file: " + log)
134596ccc8cbSesaxe			sys.exit(1)
134696ccc8cbSesaxe
1347*ccac5ae3SJosef 'Jeff' Sipek		dateTimeStr= "# %04d-%02d-%02d at %02d:%02d:%02d" % time.localtime()[:6]
134896ccc8cbSesaxe		v_info("# This file was produced by wsdiff")
134996ccc8cbSesaxe		v_info(dateTimeStr)
135096ccc8cbSesaxe
1351598cc7dfSVladimir Kotal	# Changed files (used only for the sorted case)
1352598cc7dfSVladimir Kotal	if sorted :
1353598cc7dfSVladimir Kotal		differentFiles = []
1354598cc7dfSVladimir Kotal
135596ccc8cbSesaxe	#
135696ccc8cbSesaxe	# Build paths to the tools required tools
135796ccc8cbSesaxe	#
135896ccc8cbSesaxe	# Try to look for tools in $SRC/tools if the "-t" option
135996ccc8cbSesaxe	# was specified
136096ccc8cbSesaxe	#
136196ccc8cbSesaxe	arch = commands.getoutput("uname -p")
136296ccc8cbSesaxe	if localTools :
136396ccc8cbSesaxe		try:
136496ccc8cbSesaxe			src = os.environ['SRC']
136596ccc8cbSesaxe		except:
136696ccc8cbSesaxe			error("-t specified, but $SRC not set. Cannot find $SRC/tools")
136796ccc8cbSesaxe			src = ""
136896ccc8cbSesaxe		if len(src) > 0 :
136996ccc8cbSesaxe			wsdiff_path.insert(0, src + "/tools/proto/opt/onbld/bin")
137096ccc8cbSesaxe
137196ccc8cbSesaxe	lintdump_cmd = find_tool("lintdump")
137296ccc8cbSesaxe	elfdump_cmd = find_tool("elfdump")
137396ccc8cbSesaxe	dump_cmd = find_tool("dump")
137496ccc8cbSesaxe	od_cmd = find_tool("od")
137596ccc8cbSesaxe	dis_cmd = find_tool("dis")
137696ccc8cbSesaxe	diff_cmd = find_tool("diff")
137796ccc8cbSesaxe	sqlite_cmd = find_tool("sqlite")
137896ccc8cbSesaxe
137996ccc8cbSesaxe	#
1380598cc7dfSVladimir Kotal	# Set resource limit for number of open files as high as possible.
1381598cc7dfSVladimir Kotal	# This might get handy with big number of threads.
1382598cc7dfSVladimir Kotal	#
1383598cc7dfSVladimir Kotal	(nofile_soft, nofile_hard) = resource.getrlimit(resource.RLIMIT_NOFILE)
1384598cc7dfSVladimir Kotal	try:
1385598cc7dfSVladimir Kotal		resource.setrlimit(resource.RLIMIT_NOFILE,
1386598cc7dfSVladimir Kotal		    (nofile_hard, nofile_hard))
1387598cc7dfSVladimir Kotal	except:
1388598cc7dfSVladimir Kotal		error("cannot set resource limits for number of open files")
1389598cc7dfSVladimir Kotal		sys.exit(1)
1390598cc7dfSVladimir Kotal
1391598cc7dfSVladimir Kotal	#
139296ccc8cbSesaxe	# validate the base and patch paths
139396ccc8cbSesaxe	#
139496ccc8cbSesaxe	if baseRoot[-1] != '/' :
139596ccc8cbSesaxe		baseRoot += '/'
139696ccc8cbSesaxe
139796ccc8cbSesaxe	if ptchRoot[-1] != '/' :
139896ccc8cbSesaxe		ptchRoot += '/'
139996ccc8cbSesaxe
140096ccc8cbSesaxe	if not os.path.exists(baseRoot) :
140196ccc8cbSesaxe		error("old proto area: " + baseRoot + " does not exist")
140296ccc8cbSesaxe		sys.exit(1)
140396ccc8cbSesaxe
140496ccc8cbSesaxe	if not os.path.exists(ptchRoot) :
140596ccc8cbSesaxe		error("new proto area: " + ptchRoot + \
140696ccc8cbSesaxe		      " does not exist")
140796ccc8cbSesaxe		sys.exit(1)
140896ccc8cbSesaxe
140996ccc8cbSesaxe	#
141096ccc8cbSesaxe	# log some information identifying the run
141196ccc8cbSesaxe	#
141296ccc8cbSesaxe	v_info("Old proto area: " + baseRoot)
141396ccc8cbSesaxe	v_info("New proto area: " + ptchRoot)
141496ccc8cbSesaxe	v_info("Results file: " + results + "\n")
141596ccc8cbSesaxe
141696ccc8cbSesaxe	#
141796ccc8cbSesaxe	# Set up the temporary directories / files
141896ccc8cbSesaxe	# Could use python's tmpdir routines, but these should
141996ccc8cbSesaxe	# be easier to identify / keep around for debugging
142096ccc8cbSesaxe	pid = os.getpid()
142196ccc8cbSesaxe	tmpDir1 = "/tmp/wsdiff_tmp1_" + str(pid) + "/"
142296ccc8cbSesaxe	tmpDir2 = "/tmp/wsdiff_tmp2_" + str(pid) + "/"
1423598cc7dfSVladimir Kotal	try:
142496ccc8cbSesaxe		os.makedirs(tmpDir1)
1425598cc7dfSVladimir Kotal	except OSError, e:
1426598cc7dfSVladimir Kotal		error("main: makedir failed %s" % e)
1427598cc7dfSVladimir Kotal	try:
142896ccc8cbSesaxe		os.makedirs(tmpDir2)
1429598cc7dfSVladimir Kotal	except OSError, e:
1430598cc7dfSVladimir Kotal		error("main: makedir failed %s" % e)
143196ccc8cbSesaxe
143296ccc8cbSesaxe	# Derive a catalog of new, deleted, and to-be-compared objects
143396ccc8cbSesaxe	# either from the specified base and patch proto areas, or from
143496ccc8cbSesaxe	# from an input file list
143596ccc8cbSesaxe	newOrDeleted = False
143696ccc8cbSesaxe
143796ccc8cbSesaxe	if fileNamesFile != "" :
143896ccc8cbSesaxe		changedFiles, newFiles, deletedFiles = \
143996ccc8cbSesaxe			      flistCatalog(baseRoot, ptchRoot, fileNamesFile)
144096ccc8cbSesaxe	else :
1441598cc7dfSVladimir Kotal		changedFiles, newFiles, deletedFiles = \
1442598cc7dfSVladimir Kotal				protoCatalog(baseRoot, ptchRoot)
144396ccc8cbSesaxe
144496ccc8cbSesaxe	if len(newFiles) > 0 :
144596ccc8cbSesaxe		newOrDeleted = True
144696ccc8cbSesaxe		info("\nNew objects found: ")
144796ccc8cbSesaxe
1448598cc7dfSVladimir Kotal		if sorted :
1449598cc7dfSVladimir Kotal			newFiles.sort()
145096ccc8cbSesaxe		for fn in newFiles :
145196ccc8cbSesaxe			info(fnFormat(fn))
145296ccc8cbSesaxe
145396ccc8cbSesaxe	if len(deletedFiles) > 0 :
145496ccc8cbSesaxe		newOrDeleted = True
145596ccc8cbSesaxe		info("\nObjects removed: ")
145696ccc8cbSesaxe
1457598cc7dfSVladimir Kotal		if sorted :
1458598cc7dfSVladimir Kotal			deletedFiles.sort()
145996ccc8cbSesaxe		for fn in deletedFiles :
146096ccc8cbSesaxe			info(fnFormat(fn))
146196ccc8cbSesaxe
146296ccc8cbSesaxe	if newOrDeleted :
1463598cc7dfSVladimir Kotal		info("\nChanged objects: ")
1464598cc7dfSVladimir Kotal	if sorted :
1465598cc7dfSVladimir Kotal		debug("The list will appear after the processing is done")
146696ccc8cbSesaxe
146796ccc8cbSesaxe	# Here's where all the heavy lifting happens
146896ccc8cbSesaxe	# Perform a comparison on each object appearing in
146996ccc8cbSesaxe	# both proto areas. compareOneFile will examine the
147096ccc8cbSesaxe	# file types of each object, and will vector off to
147196ccc8cbSesaxe	# the appropriate comparison routine, where the compare
147296ccc8cbSesaxe	# will happen, and any differences will be reported / logged
147396ccc8cbSesaxe
1474598cc7dfSVladimir Kotal	# determine maximum number of worker threads by using
1475598cc7dfSVladimir Kotal	# DMAKE_MAX_JOBS environment variable set by nightly(1)
1476598cc7dfSVladimir Kotal	# or get number of CPUs in the system
1477598cc7dfSVladimir Kotal	try:
1478598cc7dfSVladimir Kotal		max_threads = int(os.environ['DMAKE_MAX_JOBS'])
1479598cc7dfSVladimir Kotal	except:
1480598cc7dfSVladimir Kotal		max_threads = os.sysconf("SC_NPROCESSORS_ONLN")
1481598cc7dfSVladimir Kotal		# If we cannot get number of online CPUs in the system
1482598cc7dfSVladimir Kotal		# run unparallelized otherwise bump the number up 20%
1483598cc7dfSVladimir Kotal		# to achieve best results.
1484598cc7dfSVladimir Kotal		if max_threads == -1 :
1485598cc7dfSVladimir Kotal			max_threads = 1
1486598cc7dfSVladimir Kotal		else :
1487598cc7dfSVladimir Kotal			max_threads += max_threads/5
1488598cc7dfSVladimir Kotal
1489598cc7dfSVladimir Kotal	# Set signal handler to attempt graceful exit
1490598cc7dfSVladimir Kotal	debug("Setting signal handler")
1491598cc7dfSVladimir Kotal	signal.signal( signal.SIGINT, discontinue_processing )
1492598cc7dfSVladimir Kotal
1493598cc7dfSVladimir Kotal	# Create and unleash the threads
1494598cc7dfSVladimir Kotal	# Only at most max_threads must be running at any moment
1495598cc7dfSVladimir Kotal	mythreads = []
1496598cc7dfSVladimir Kotal	debug("Spawning " + str(max_threads) + " threads");
1497598cc7dfSVladimir Kotal	for i in range(max_threads) :
1498598cc7dfSVladimir Kotal		thread = workerThread()
1499598cc7dfSVladimir Kotal		mythreads.append(thread)
1500598cc7dfSVladimir Kotal		mythreads[i].start()
1501598cc7dfSVladimir Kotal
1502598cc7dfSVladimir Kotal	# Wait for the threads to finish and do cleanup if interrupted
1503598cc7dfSVladimir Kotal	debug("Waiting for the threads to finish")
1504598cc7dfSVladimir Kotal	while True:
1505598cc7dfSVladimir Kotal		if not True in [thread.isAlive() for thread in mythreads]:
1506598cc7dfSVladimir Kotal		    break
1507598cc7dfSVladimir Kotal		else:
1508598cc7dfSVladimir Kotal		    # Some threads are still going
1509598cc7dfSVladimir Kotal		    time.sleep(1)
1510598cc7dfSVladimir Kotal
1511598cc7dfSVladimir Kotal	# Interrupted by SIGINT
1512598cc7dfSVladimir Kotal	if keep_processing == False :
1513598cc7dfSVladimir Kotal		cleanup(1)
1514598cc7dfSVladimir Kotal
1515598cc7dfSVladimir Kotal	# If the list of differences was sorted it is stored in an array
1516598cc7dfSVladimir Kotal	if sorted :
1517598cc7dfSVladimir Kotal		differentFiles.sort()
1518598cc7dfSVladimir Kotal		for f in differentFiles :
1519598cc7dfSVladimir Kotal			info(fnFormat(f))
152096ccc8cbSesaxe
152196ccc8cbSesaxe	# We're done, cleanup.
152296ccc8cbSesaxe	cleanup(0)
152396ccc8cbSesaxe
152496ccc8cbSesaxeif __name__ == '__main__' :
152596ccc8cbSesaxe	try:
152696ccc8cbSesaxe		main()
152796ccc8cbSesaxe	except KeyboardInterrupt :
152896ccc8cbSesaxe		cleanup(1);
152996ccc8cbSesaxe
153096ccc8cbSesaxe
1531