1*12887SVladimir.Kotal@Sun.COM#!/usr/bin/python2.6 22538Sesaxe# 32538Sesaxe# CDDL HEADER START 42538Sesaxe# 52538Sesaxe# The contents of this file are subject to the terms of the 62538Sesaxe# Common Development and Distribution License (the "License"). 72538Sesaxe# You may not use this file except in compliance with the License. 82538Sesaxe# 92538Sesaxe# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 102538Sesaxe# or http://www.opensolaris.org/os/licensing. 112538Sesaxe# See the License for the specific language governing permissions 122538Sesaxe# and limitations under the License. 132538Sesaxe# 142538Sesaxe# When distributing Covered Code, include this CDDL HEADER in each 152538Sesaxe# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 162538Sesaxe# If applicable, add the following below this CDDL HEADER, with the 172538Sesaxe# fields enclosed by brackets "[]" replaced with your own identifying 182538Sesaxe# information: Portions Copyright [yyyy] [name of copyright owner] 192538Sesaxe# 202538Sesaxe# CDDL HEADER END 212538Sesaxe# 22*12887SVladimir.Kotal@Sun.COM# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 232538Sesaxe# 242538Sesaxe 252538Sesaxe# 262538Sesaxe# wsdiff(1) is a tool that can be used to determine which compiled objects 272538Sesaxe# have changed as a result of a given source change. Developers backporting 282538Sesaxe# new features, RFEs and bug fixes need to be able to identify the set of 292538Sesaxe# patch deliverables necessary for feature/fix realization on a patched system. 302538Sesaxe# 312538Sesaxe# The tool works by comparing objects in two trees/proto areas (one build with, 322538Sesaxe# and without the source changes. 332538Sesaxe# 342538Sesaxe# Using wsdiff(1) is fairly simple: 352538Sesaxe# - Bringover to a fresh workspace 362538Sesaxe# - Perform a full non-debug build (clobber if workspace isn't fresh) 372538Sesaxe# - Move the proto area aside, call it proto.old, or something. 382538Sesaxe# - Integrate your changes to the workspace 392538Sesaxe# - Perform another full non-debug clobber build. 402538Sesaxe# - Use wsdiff(1) to see what changed: 412538Sesaxe# $ wsdiff proto.old proto 422538Sesaxe# 432538Sesaxe# By default, wsdiff will print the list of changed objects / deliverables to 442538Sesaxe# stdout. If a results file is specified via -r, the list of differing objects, 452538Sesaxe# and details about why wsdiff(1) thinks they are different will be logged to 462538Sesaxe# the results file. 472538Sesaxe# 482538Sesaxe# By invoking nightly(1) with the -w option to NIGHTLY_FLAGS, nightly(1) will use 492538Sesaxe# wsdiff(1) to report on what objects changed since the last build. 502538Sesaxe# 512538Sesaxe# For patch deliverable purposes, it's advised to have nightly do a clobber, 522538Sesaxe# non-debug build. 532538Sesaxe# 542538Sesaxe# Think about the results. Was something flagged that you don't expect? Go look 552538Sesaxe# at the results file to see details about the differences. 562538Sesaxe# 572538Sesaxe# Use the -i option in conjunction with -v and -V to dive deeper and have wsdiff(1) 582538Sesaxe# report with more verbosity. 592538Sesaxe# 602538Sesaxe# Usage: wsdiff [-vVt] [-r results ] [-i filelist ] old new 612538Sesaxe# 622538Sesaxe# Where "old" is the path to the proto area build without the changes, and 632538Sesaxe# "new" is the path to the proto area built with the changes. The following 642538Sesaxe# options are supported: 652538Sesaxe# 662538Sesaxe# -v Do not truncate observed diffs in results 672538Sesaxe# -V Log *all* ELF sect diffs vs. logging the first diff found 682538Sesaxe# -t Use onbld tools in $SRC/tools 692538Sesaxe# -r Log results and observed differences 702538Sesaxe# -i Tell wsdiff which objects to compare via an input file list 712538Sesaxe 72*12887SVladimir.Kotal@Sun.COMimport datetime, fnmatch, getopt, os, profile, commands 73*12887SVladimir.Kotal@Sun.COMimport re, resource, select, shutil, signal, string, struct, sys, tempfile 74*12887SVladimir.Kotal@Sun.COMimport time, threading 752538Sesaxefrom stat import * 762538Sesaxe 772538Sesaxe# Human readable diffs truncated by default if longer than this 782538Sesaxe# Specifying -v on the command line will override 792538Sesaxediffs_sz_thresh = 4096 802538Sesaxe 81*12887SVladimir.Kotal@Sun.COM# Lock name Provides exclusive access to 82*12887SVladimir.Kotal@Sun.COM# --------------+------------------------------------------------ 83*12887SVladimir.Kotal@Sun.COM# output_lock standard output or temporary file (difference()) 84*12887SVladimir.Kotal@Sun.COM# log_lock the results file (log_difference()) 85*12887SVladimir.Kotal@Sun.COM# wset_lock changedFiles list (workerThread()) 86*12887SVladimir.Kotal@Sun.COMoutput_lock = threading.Lock() 87*12887SVladimir.Kotal@Sun.COMlog_lock = threading.Lock() 88*12887SVladimir.Kotal@Sun.COMwset_lock = threading.Lock() 89*12887SVladimir.Kotal@Sun.COM 90*12887SVladimir.Kotal@Sun.COM# Variable for thread control 91*12887SVladimir.Kotal@Sun.COMkeep_processing = True 92*12887SVladimir.Kotal@Sun.COM 932538Sesaxe# Default search path for wsdiff 942538Sesaxewsdiff_path = [ "/usr/bin", 952538Sesaxe "/usr/ccs/bin", 962538Sesaxe "/lib/svc/bin", 972538Sesaxe "/opt/onbld/bin" ] 982538Sesaxe 992538Sesaxe# These are objects that wsdiff will notice look different, but will not report. 1002538Sesaxe# Existence of an exceptions list, and adding things here is *dangerous*, 1012538Sesaxe# and therefore the *only* reasons why anything would be listed here is because 1022538Sesaxe# the objects do not build deterministically, yet we *cannot* fix this. 1032538Sesaxe# 1042538Sesaxe# These perl libraries use __DATE__ and therefore always look different. 1052538Sesaxe# Ideally, we would purge use the use of __DATE__ from the source, but because 1062538Sesaxe# this is source we wish to distribute with Solaris "unchanged", we cannot modify. 1072538Sesaxe# 1082538Sesaxewsdiff_exceptions = [ "usr/perl5/5.8.4/lib/sun4-solaris-64int/CORE/libperl.so.1", 1092538Sesaxe "usr/perl5/5.6.1/lib/sun4-solaris-64int/CORE/libperl.so.1", 1102538Sesaxe "usr/perl5/5.8.4/lib/i86pc-solaris-64int/CORE/libperl.so.1", 1112538Sesaxe "usr/perl5/5.6.1/lib/i86pc-solaris-64int/CORE/libperl.so.1" 1122538Sesaxe ] 1132538Sesaxe 1142538Sesaxe##### 1152538Sesaxe# Logging routines 1162538Sesaxe# 1172538Sesaxe 118*12887SVladimir.Kotal@Sun.COM# Debug message to be printed to the screen, and the log file 119*12887SVladimir.Kotal@Sun.COMdef debug(msg) : 120*12887SVladimir.Kotal@Sun.COM 121*12887SVladimir.Kotal@Sun.COM # Add prefix to highlight debugging message 122*12887SVladimir.Kotal@Sun.COM msg = "## " + msg 123*12887SVladimir.Kotal@Sun.COM if debugon : 124*12887SVladimir.Kotal@Sun.COM output_lock.acquire() 125*12887SVladimir.Kotal@Sun.COM print >> sys.stdout, msg 126*12887SVladimir.Kotal@Sun.COM sys.stdout.flush() 127*12887SVladimir.Kotal@Sun.COM output_lock.release() 128*12887SVladimir.Kotal@Sun.COM if logging : 129*12887SVladimir.Kotal@Sun.COM log_lock.acquire() 130*12887SVladimir.Kotal@Sun.COM print >> log, msg 131*12887SVladimir.Kotal@Sun.COM log.flush() 132*12887SVladimir.Kotal@Sun.COM log_lock.release() 133*12887SVladimir.Kotal@Sun.COM 1342538Sesaxe# Informational message to be printed to the screen, and the log file 1352538Sesaxedef info(msg) : 1362538Sesaxe 137*12887SVladimir.Kotal@Sun.COM output_lock.acquire() 1382538Sesaxe print >> sys.stdout, msg 139*12887SVladimir.Kotal@Sun.COM sys.stdout.flush() 140*12887SVladimir.Kotal@Sun.COM output_lock.release() 1412538Sesaxe if logging : 142*12887SVladimir.Kotal@Sun.COM log_lock.acquire() 1432538Sesaxe print >> log, msg 144*12887SVladimir.Kotal@Sun.COM log.flush() 145*12887SVladimir.Kotal@Sun.COM log_lock.release() 1462538Sesaxe 1472538Sesaxe# Error message to be printed to the screen, and the log file 1482538Sesaxedef error(msg) : 149*12887SVladimir.Kotal@Sun.COM 150*12887SVladimir.Kotal@Sun.COM output_lock.acquire() 1512538Sesaxe print >> sys.stderr, "ERROR:", msg 1522538Sesaxe sys.stderr.flush() 153*12887SVladimir.Kotal@Sun.COM output_lock.release() 1542538Sesaxe if logging : 155*12887SVladimir.Kotal@Sun.COM log_lock.acquire() 1562538Sesaxe print >> log, "ERROR:", msg 1572538Sesaxe log.flush() 158*12887SVladimir.Kotal@Sun.COM log_lock.release() 1592538Sesaxe 1602538Sesaxe# Informational message to be printed only to the log, if there is one. 1612538Sesaxedef v_info(msg) : 1622538Sesaxe 1632538Sesaxe if logging : 164*12887SVladimir.Kotal@Sun.COM log_lock.acquire() 1652538Sesaxe print >> log, msg 1662538Sesaxe log.flush() 167*12887SVladimir.Kotal@Sun.COM log_lock.release() 168*12887SVladimir.Kotal@Sun.COM 1692538Sesaxe# 1702538Sesaxe# Flag a detected file difference 1712538Sesaxe# Display the fileName to stdout, and log the difference 1722538Sesaxe# 1732538Sesaxedef difference(f, dtype, diffs) : 1742538Sesaxe 1752538Sesaxe if f in wsdiff_exceptions : 1762538Sesaxe return 1772538Sesaxe 178*12887SVladimir.Kotal@Sun.COM output_lock.acquire() 179*12887SVladimir.Kotal@Sun.COM if sorted : 180*12887SVladimir.Kotal@Sun.COM differentFiles.append(f) 181*12887SVladimir.Kotal@Sun.COM else: 182*12887SVladimir.Kotal@Sun.COM print >> sys.stdout, f 183*12887SVladimir.Kotal@Sun.COM sys.stdout.flush() 184*12887SVladimir.Kotal@Sun.COM output_lock.release() 1852538Sesaxe 1862538Sesaxe log_difference(f, dtype, diffs) 1872538Sesaxe 1882538Sesaxe# 1892538Sesaxe# Do the actual logging of the difference to the results file 1902538Sesaxe# 1912538Sesaxedef log_difference(f, dtype, diffs) : 192*12887SVladimir.Kotal@Sun.COM 1932538Sesaxe if logging : 194*12887SVladimir.Kotal@Sun.COM log_lock.acquire() 1952538Sesaxe print >> log, f 1962538Sesaxe print >> log, "NOTE:", dtype, "difference detected." 1972538Sesaxe 1982538Sesaxe difflen = len(diffs) 19911300Srichlowe@richlowe.net if difflen > 0 : 2002538Sesaxe print >> log 2012538Sesaxe 2022538Sesaxe if not vdiffs and difflen > diffs_sz_thresh : 2032538Sesaxe print >> log, diffs[:diffs_sz_thresh] 2042538Sesaxe print >> log, \ 2052538Sesaxe "... truncated due to length: " \ 2062538Sesaxe "use -v to override ..." 2072538Sesaxe else : 2082538Sesaxe print >> log, diffs 2092538Sesaxe print >> log, "\n" 2102538Sesaxe log.flush() 211*12887SVladimir.Kotal@Sun.COM log_lock.release() 2122538Sesaxe 2132538Sesaxe 2142538Sesaxe##### 2152538Sesaxe# diff generating routines 2162538Sesaxe# 2172538Sesaxe 2182538Sesaxe# 2192538Sesaxe# Return human readable diffs from two temporary files 2202538Sesaxe# 2212538Sesaxedef diffFileData(tmpf1, tmpf2) : 2222538Sesaxe 223*12887SVladimir.Kotal@Sun.COM binaries = False 224*12887SVladimir.Kotal@Sun.COM 2252538Sesaxe # Filter the data through od(1) if the data is detected 2262538Sesaxe # as being binary 2272538Sesaxe if isBinary(tmpf1) or isBinary(tmpf2) : 228*12887SVladimir.Kotal@Sun.COM binaries = True 2292538Sesaxe tmp_od1 = tmpf1 + ".od" 2302538Sesaxe tmp_od2 = tmpf2 + ".od" 231*12887SVladimir.Kotal@Sun.COM 2322538Sesaxe cmd = od_cmd + " -c -t x4" + " " + tmpf1 + " > " + tmp_od1 2332538Sesaxe os.system(cmd) 2342538Sesaxe cmd = od_cmd + " -c -t x4" + " " + tmpf2 + " > " + tmp_od2 2352538Sesaxe os.system(cmd) 236*12887SVladimir.Kotal@Sun.COM 2372538Sesaxe tmpf1 = tmp_od1 2382538Sesaxe tmpf2 = tmp_od2 2392538Sesaxe 240*12887SVladimir.Kotal@Sun.COM try: 241*12887SVladimir.Kotal@Sun.COM data = commands.getoutput(diff_cmd + " " + tmpf1 + " " + tmpf2) 242*12887SVladimir.Kotal@Sun.COM # Remove the temp files as we no longer need them. 243*12887SVladimir.Kotal@Sun.COM if binaries : 244*12887SVladimir.Kotal@Sun.COM try: 245*12887SVladimir.Kotal@Sun.COM os.unlink(tmp_od1) 246*12887SVladimir.Kotal@Sun.COM except OSError, e: 247*12887SVladimir.Kotal@Sun.COM error("diffFileData: unlink failed %s" % e) 248*12887SVladimir.Kotal@Sun.COM try: 249*12887SVladimir.Kotal@Sun.COM os.unlink(tmp_od2) 250*12887SVladimir.Kotal@Sun.COM except OSError, e: 251*12887SVladimir.Kotal@Sun.COM error("diffFileData: unlink failed %s" % e) 252*12887SVladimir.Kotal@Sun.COM except: 253*12887SVladimir.Kotal@Sun.COM error("failed to get output of command: " + diff_cmd + " " \ 254*12887SVladimir.Kotal@Sun.COM + tmpf1 + " " + tmpf2) 255*12887SVladimir.Kotal@Sun.COM 256*12887SVladimir.Kotal@Sun.COM # Send exception for the failed command up 257*12887SVladimir.Kotal@Sun.COM raise 258*12887SVladimir.Kotal@Sun.COM return 2592538Sesaxe 2602538Sesaxe return data 2612538Sesaxe 2622538Sesaxe# 2632538Sesaxe# Return human readable diffs betweeen two datasets 2642538Sesaxe# 265*12887SVladimir.Kotal@Sun.COMdef diffData(base, ptch, d1, d2) : 2662538Sesaxe 267*12887SVladimir.Kotal@Sun.COM t = threading.currentThread() 268*12887SVladimir.Kotal@Sun.COM tmpFile1 = tmpDir1 + os.path.basename(base) + t.getName() 269*12887SVladimir.Kotal@Sun.COM tmpFile2 = tmpDir2 + os.path.basename(ptch) + t.getName() 2702538Sesaxe 2712538Sesaxe try: 2722538Sesaxe fd1 = open(tmpFile1, "w") 2732538Sesaxe except: 2742538Sesaxe error("failed to open: " + tmpFile1) 2752538Sesaxe cleanup(1) 276*12887SVladimir.Kotal@Sun.COM 2772538Sesaxe try: 2782538Sesaxe fd2 = open(tmpFile2, "w") 2792538Sesaxe except: 2802538Sesaxe error("failed to open: " + tmpFile2) 2812538Sesaxe cleanup(1) 2822538Sesaxe 2832538Sesaxe fd1.write(d1) 2842538Sesaxe fd2.write(d2) 2852538Sesaxe fd1.close() 2862538Sesaxe fd2.close() 2872538Sesaxe 2882538Sesaxe return diffFileData(tmpFile1, tmpFile2) 2892538Sesaxe 2902538Sesaxe##### 2912538Sesaxe# Misc utility functions 2922538Sesaxe# 2932538Sesaxe 2942538Sesaxe# Prune off the leading prefix from string s 2952538Sesaxedef str_prefix_trunc(s, prefix) : 2962538Sesaxe snipLen = len(prefix) 2972538Sesaxe return s[snipLen:] 2982538Sesaxe 2992538Sesaxe# 3002538Sesaxe# Prune off leading proto path goo (if there is one) to yield 3012538Sesaxe# the deliverable's eventual path relative to root 3022538Sesaxe# e.g. proto.base/root_sparc/usr/src/cmd/prstat => usr/src/cmd/prstat 30311300Srichlowe@richlowe.net# 3042538Sesaxedef fnFormat(fn) : 3052538Sesaxe root_arch_str = "root_" + arch 30611300Srichlowe@richlowe.net 3072538Sesaxe pos = fn.find(root_arch_str) 3082538Sesaxe if pos == -1 : 3092538Sesaxe return fn 3102538Sesaxe 3112538Sesaxe pos = fn.find("/", pos) 3122538Sesaxe if pos == -1 : 3132538Sesaxe return fn 3142538Sesaxe 3152538Sesaxe return fn[pos + 1:] 3162538Sesaxe 3172538Sesaxe##### 3182538Sesaxe# Usage / argument processing 3192538Sesaxe# 3202538Sesaxe 3212538Sesaxe# 3222538Sesaxe# Display usage message 3232538Sesaxe# 3242538Sesaxedef usage() : 3252538Sesaxe sys.stdout.flush() 326*12887SVladimir.Kotal@Sun.COM print >> sys.stderr, """Usage: wsdiff [-dvVst] [-r results ] [-i filelist ] old new 327*12887SVladimir.Kotal@Sun.COM -d Print debug messages about the progress 3282538Sesaxe -v Do not truncate observed diffs in results 3292538Sesaxe -V Log *all* ELF sect diffs vs. logging the first diff found 3302538Sesaxe -t Use onbld tools in $SRC/tools 3312538Sesaxe -r Log results and observed differences 332*12887SVladimir.Kotal@Sun.COM -s Produce sorted list of differences 3332538Sesaxe -i Tell wsdiff which objects to compare via an input file list""" 3342538Sesaxe sys.exit(1) 3352538Sesaxe 3362538Sesaxe# 3372538Sesaxe# Process command line options 3382538Sesaxe# 3392538Sesaxedef args() : 3402538Sesaxe 341*12887SVladimir.Kotal@Sun.COM global debugon 3422538Sesaxe global logging 3432538Sesaxe global vdiffs 3442538Sesaxe global reportAllSects 345*12887SVladimir.Kotal@Sun.COM global sorted 3462538Sesaxe 347*12887SVladimir.Kotal@Sun.COM validOpts = 'di:r:vVst?' 3482538Sesaxe 3492538Sesaxe baseRoot = "" 3502538Sesaxe ptchRoot = "" 3512538Sesaxe fileNamesFile = "" 3522538Sesaxe results = "" 3532538Sesaxe localTools = False 3542538Sesaxe 3552538Sesaxe # getopt.getopt() returns: 3562538Sesaxe # an option/value tuple 3572538Sesaxe # a list of remaining non-option arguments 3582538Sesaxe # 3592538Sesaxe # A correct wsdiff invocation will have exactly two non option 3602538Sesaxe # arguments, the paths to the base (old), ptch (new) proto areas 3612538Sesaxe try: 3622538Sesaxe optlist, args = getopt.getopt(sys.argv[1:], validOpts) 3632538Sesaxe except getopt.error, val: 3642538Sesaxe usage() 3652538Sesaxe 3662538Sesaxe if len(args) != 2 : 3672538Sesaxe usage(); 3682538Sesaxe 3692538Sesaxe for opt,val in optlist : 370*12887SVladimir.Kotal@Sun.COM if opt == '-d' : 371*12887SVladimir.Kotal@Sun.COM debugon = True 372*12887SVladimir.Kotal@Sun.COM elif opt == '-i' : 3732538Sesaxe fileNamesFile = val 3742538Sesaxe elif opt == '-r' : 3752538Sesaxe results = val 3762538Sesaxe logging = True 377*12887SVladimir.Kotal@Sun.COM elif opt == '-s' : 378*12887SVladimir.Kotal@Sun.COM sorted = True 3792538Sesaxe elif opt == '-v' : 3802538Sesaxe vdiffs = True 3812538Sesaxe elif opt == '-V' : 3822538Sesaxe reportAllSects = True 3832538Sesaxe elif opt == '-t': 3842538Sesaxe localTools = True 3852538Sesaxe else: 3862538Sesaxe usage() 3872538Sesaxe 3882538Sesaxe baseRoot = args[0] 3892538Sesaxe ptchRoot = args[1] 3902538Sesaxe 3912538Sesaxe if len(baseRoot) == 0 or len(ptchRoot) == 0 : 3922538Sesaxe usage() 3932538Sesaxe 3942538Sesaxe if logging and len(results) == 0 : 3952538Sesaxe usage() 3962538Sesaxe 3972538Sesaxe if vdiffs and not logging : 3982538Sesaxe error("The -v option requires a results file (-r)") 3992538Sesaxe sys.exit(1) 4002538Sesaxe 4012538Sesaxe if reportAllSects and not logging : 4022538Sesaxe error("The -V option requires a results file (-r)") 4032538Sesaxe sys.exit(1) 4042538Sesaxe 4052538Sesaxe # alphabetical order 4062538Sesaxe return baseRoot, fileNamesFile, localTools, ptchRoot, results 4072538Sesaxe 4082538Sesaxe##### 4092538Sesaxe# File identification 4102538Sesaxe# 4112538Sesaxe 4122538Sesaxe# 4132538Sesaxe# Identify the file type. 4142538Sesaxe# If it's not ELF, use the file extension to identify 4152538Sesaxe# certain file types that require special handling to 4162538Sesaxe# compare. Otherwise just return a basic "ASCII" type. 4172538Sesaxe# 4182538Sesaxedef getTheFileType(f) : 4192538Sesaxe 4202538Sesaxe extensions = { 'a' : 'ELF Object Archive', 4212538Sesaxe 'jar' : 'Java Archive', 4222538Sesaxe 'html' : 'HTML', 4232538Sesaxe 'ln' : 'Lint Library', 4242538Sesaxe 'db' : 'Sqlite Database' } 42511300Srichlowe@richlowe.net 4265298Srotondo try: 4275298Srotondo if os.stat(f)[ST_SIZE] == 0 : 4285298Srotondo return 'ASCII' 4295298Srotondo except: 4305298Srotondo error("failed to stat " + f) 4315298Srotondo return 'Error' 4322538Sesaxe 4332538Sesaxe if isELF(f) == 1 : 4342538Sesaxe return 'ELF' 4352538Sesaxe 4362538Sesaxe fnamelist = f.split('.') 4372538Sesaxe if len(fnamelist) > 1 : # Test the file extension 4382538Sesaxe extension = fnamelist[-1] 4392538Sesaxe if extension in extensions.keys(): 4402538Sesaxe return extensions[extension] 4412538Sesaxe 4422538Sesaxe return 'ASCII' 4432538Sesaxe 4442538Sesaxe# 4452538Sesaxe# Return non-zero if "f" is an ELF file 4462538Sesaxe# 4472538Sesaxeelfmagic = '\177ELF' 4482538Sesaxedef isELF(f) : 4492538Sesaxe try: 4502538Sesaxe fd = open(f) 4512538Sesaxe except: 4522538Sesaxe error("failed to open: " + f) 4532538Sesaxe return 0 4542538Sesaxe magic = fd.read(len(elfmagic)) 4552538Sesaxe fd.close() 4562538Sesaxe 4572538Sesaxe if magic == elfmagic : 4582538Sesaxe return 1 4592538Sesaxe return 0 4602538Sesaxe 4612538Sesaxe# 4622538Sesaxe# Return non-zero is "f" is binary. 4632538Sesaxe# Consider the file to be binary if it contains any null characters 46411300Srichlowe@richlowe.net# 4652538Sesaxedef isBinary(f) : 4662538Sesaxe try: 4672538Sesaxe fd = open(f) 4682538Sesaxe except: 4692538Sesaxe error("failed to open: " + f) 4702538Sesaxe return 0 4712538Sesaxe s = fd.read() 4722538Sesaxe fd.close() 4732538Sesaxe 4742538Sesaxe if s.find('\0') == -1 : 4752538Sesaxe return 0 4762538Sesaxe else : 4772538Sesaxe return 1 4782538Sesaxe 4792538Sesaxe##### 4802538Sesaxe# Directory traversal and file finding 48111300Srichlowe@richlowe.net# 4822538Sesaxe 4832538Sesaxe# 4842538Sesaxe# Return a sorted list of files found under the specified directory 4852538Sesaxe# 4862538Sesaxedef findFiles(d) : 4872538Sesaxe for path, subdirs, files in os.walk(d) : 4882538Sesaxe files.sort() 4892538Sesaxe for name in files : 4902538Sesaxe yield os.path.join(path, name) 4912538Sesaxe 4922538Sesaxe# 4932538Sesaxe# Examine all files in base, ptch 4942538Sesaxe# 4952538Sesaxe# Return a list of files appearing in both proto areas, 4962538Sesaxe# a list of new files (files found only in ptch) and 4972538Sesaxe# a list of deleted files (files found only in base) 4982538Sesaxe# 4992538Sesaxedef protoCatalog(base, ptch) : 500*12887SVladimir.Kotal@Sun.COM 5012538Sesaxe compFiles = [] # List of files in both proto areas 5022538Sesaxe ptchList = [] # List of file in patch proto area 5032538Sesaxe 5042538Sesaxe newFiles = [] # New files detected 5052538Sesaxe deletedFiles = [] # Deleted files 50611300Srichlowe@richlowe.net 507*12887SVladimir.Kotal@Sun.COM debug("Getting the list of files in the base area"); 5082538Sesaxe baseFilesList = list(findFiles(base)) 5092538Sesaxe baseStringLength = len(base) 510*12887SVladimir.Kotal@Sun.COM debug("Found " + str(len(baseFilesList)) + " files") 511*12887SVladimir.Kotal@Sun.COM 512*12887SVladimir.Kotal@Sun.COM debug("Getting the list of files in the patch area"); 5132538Sesaxe ptchFilesList = list(findFiles(ptch)) 5142538Sesaxe ptchStringLength = len(ptch) 515*12887SVladimir.Kotal@Sun.COM debug("Found " + str(len(ptchFilesList)) + " files") 5162538Sesaxe 5172538Sesaxe # Inventory files in the base proto area 518*12887SVladimir.Kotal@Sun.COM debug("Determining the list of regular files in the base area"); 5192538Sesaxe for fn in baseFilesList : 5202538Sesaxe if os.path.islink(fn) : 5212538Sesaxe continue 5222538Sesaxe 5232538Sesaxe fileName = fn[baseStringLength:] 5242538Sesaxe compFiles.append(fileName) 525*12887SVladimir.Kotal@Sun.COM debug("Found " + str(len(compFiles)) + " files") 5262538Sesaxe 5272538Sesaxe # Inventory files in the patch proto area 528*12887SVladimir.Kotal@Sun.COM debug("Determining the list of regular files in the patch area"); 5292538Sesaxe for fn in ptchFilesList : 5302538Sesaxe if os.path.islink(fn) : 5312538Sesaxe continue 5322538Sesaxe 5332538Sesaxe fileName = fn[ptchStringLength:] 5342538Sesaxe ptchList.append(fileName) 535*12887SVladimir.Kotal@Sun.COM debug("Found " + str(len(ptchList)) + " files") 5362538Sesaxe 5372538Sesaxe # Deleted files appear in the base area, but not the patch area 538*12887SVladimir.Kotal@Sun.COM debug("Searching for deleted files by comparing the lists") 5392538Sesaxe for fileName in compFiles : 5402538Sesaxe if not fileName in ptchList : 5412538Sesaxe deletedFiles.append(fileName) 542*12887SVladimir.Kotal@Sun.COM debug("Found " + str(len(deletedFiles)) + " deleted files") 5432538Sesaxe 5442538Sesaxe # Eliminate "deleted" files from the list of objects appearing 5452538Sesaxe # in both the base and patch proto areas 546*12887SVladimir.Kotal@Sun.COM debug("Eliminating deleted files from the list of objects") 5472538Sesaxe for fileName in deletedFiles : 5482538Sesaxe try: 5492538Sesaxe compFiles.remove(fileName) 5502538Sesaxe except: 5512538Sesaxe error("filelist.remove() failed") 552*12887SVladimir.Kotal@Sun.COM debug("List for comparison reduced to " + str(len(compFiles)) \ 553*12887SVladimir.Kotal@Sun.COM + " files") 5542538Sesaxe 5552538Sesaxe # New files appear in the patch area, but not the base 556*12887SVladimir.Kotal@Sun.COM debug("Getting the list of newly added files") 5572538Sesaxe for fileName in ptchList : 5582538Sesaxe if not fileName in compFiles : 5592538Sesaxe newFiles.append(fileName) 560*12887SVladimir.Kotal@Sun.COM debug("Found " + str(len(newFiles)) + " new files") 5612538Sesaxe 5622538Sesaxe return compFiles, newFiles, deletedFiles 5632538Sesaxe 5642538Sesaxe# 5652538Sesaxe# Examine the files listed in the input file list 5662538Sesaxe# 5672538Sesaxe# Return a list of files appearing in both proto areas, 5682538Sesaxe# a list of new files (files found only in ptch) and 5692538Sesaxe# a list of deleted files (files found only in base) 5702538Sesaxe# 5712538Sesaxedef flistCatalog(base, ptch, flist) : 5722538Sesaxe compFiles = [] # List of files in both proto areas 5732538Sesaxe newFiles = [] # New files detected 5742538Sesaxe deletedFiles = [] # Deleted files 57511300Srichlowe@richlowe.net 5762538Sesaxe try: 5772538Sesaxe fd = open(flist, "r") 5782538Sesaxe except: 5792538Sesaxe error("could not open: " + flist) 5802538Sesaxe cleanup(1) 5812538Sesaxe 5822538Sesaxe files = [] 5832538Sesaxe files = fd.readlines() 584*12887SVladimir.Kotal@Sun.COM fd.close() 58511300Srichlowe@richlowe.net 5862538Sesaxe for f in files : 5872538Sesaxe ptch_present = True 5882538Sesaxe base_present = True 58911300Srichlowe@richlowe.net 5902538Sesaxe if f == '\n' : 5912538Sesaxe continue 5922538Sesaxe 5932538Sesaxe # the fileNames have a trailing '\n' 5942538Sesaxe f = f.rstrip() 5952538Sesaxe 5962538Sesaxe # The objects in the file list have paths relative 5972538Sesaxe # to $ROOT or to the base/ptch directory specified on 5982538Sesaxe # the command line. 5992538Sesaxe # If it's relative to $ROOT, we'll need to add back the 6002538Sesaxe # root_`uname -p` goo we stripped off in fnFormat() 6012538Sesaxe if os.path.exists(base + f) : 6022538Sesaxe fn = f; 6032538Sesaxe elif os.path.exists(base + "root_" + arch + "/" + f) : 6042538Sesaxe fn = "root_" + arch + "/" + f 6052538Sesaxe else : 6062538Sesaxe base_present = False 6072538Sesaxe 6082538Sesaxe if base_present : 6092538Sesaxe if not os.path.exists(ptch + fn) : 6102538Sesaxe ptch_present = False 6112538Sesaxe else : 6122538Sesaxe if os.path.exists(ptch + f) : 6132538Sesaxe fn = f 6142538Sesaxe elif os.path.exists(ptch + "root_" + arch + "/" + f) : 6152538Sesaxe fn = "root_" + arch + "/" + f 6162538Sesaxe else : 6172538Sesaxe ptch_present = False 6182538Sesaxe 6192538Sesaxe if os.path.islink(base + fn) : # ignore links 6202538Sesaxe base_present = False 6212538Sesaxe if os.path.islink(ptch + fn) : 6222538Sesaxe ptch_present = False 6232538Sesaxe 6242538Sesaxe if base_present and ptch_present : 6252538Sesaxe compFiles.append(fn) 6262538Sesaxe elif base_present : 6272538Sesaxe deletedFiles.append(fn) 6282538Sesaxe elif ptch_present : 6292538Sesaxe newFiles.append(fn) 6302538Sesaxe else : 631*12887SVladimir.Kotal@Sun.COM if os.path.islink(base + fn) and \ 632*12887SVladimir.Kotal@Sun.COM os.path.islink(ptch + fn) : 6332538Sesaxe continue 634*12887SVladimir.Kotal@Sun.COM error(f + " in file list, but not in either tree. " + \ 635*12887SVladimir.Kotal@Sun.COM "Skipping...") 63611300Srichlowe@richlowe.net 6372538Sesaxe return compFiles, newFiles, deletedFiles 6382538Sesaxe 6392538Sesaxe 6402538Sesaxe# 6412538Sesaxe# Build a fully qualified path to an external tool/utility. 6422538Sesaxe# Consider the default system locations. For onbld tools, if 6432538Sesaxe# the -t option was specified, we'll try to use built tools in $SRC tools, 6442538Sesaxe# and otherwise, we'll fall back on /opt/onbld/ 6452538Sesaxe# 6462538Sesaxedef find_tool(tool) : 6472538Sesaxe 6482538Sesaxe # First, check what was passed 6492538Sesaxe if os.path.exists(tool) : 6502538Sesaxe return tool 6512538Sesaxe 6522538Sesaxe # Next try in wsdiff path 6532538Sesaxe for pdir in wsdiff_path : 6542538Sesaxe location = pdir + "/" + tool 6552538Sesaxe if os.path.exists(location) : 6562538Sesaxe return location + " " 6572538Sesaxe 6582538Sesaxe location = pdir + "/" + arch + "/" + tool 6592538Sesaxe if os.path.exists(location) : 6602538Sesaxe return location + " " 66111300Srichlowe@richlowe.net 6622538Sesaxe error("Could not find path to: " + tool); 6632538Sesaxe sys.exit(1); 66411300Srichlowe@richlowe.net 6652538Sesaxe 6662538Sesaxe##### 6672538Sesaxe# ELF file comparison helper routines 6682538Sesaxe# 6692538Sesaxe 6702538Sesaxe# 6712538Sesaxe# Return a dictionary of ELF section types keyed by section name 67211300Srichlowe@richlowe.net# 6732538Sesaxedef get_elfheader(f) : 6742538Sesaxe 6752538Sesaxe header = {} 6762538Sesaxe 6772538Sesaxe hstring = commands.getoutput(elfdump_cmd + " -c " + f) 6782538Sesaxe 6792538Sesaxe if len(hstring) == 0 : 6802538Sesaxe error("Failed to dump ELF header for " + f) 681*12887SVladimir.Kotal@Sun.COM raise 6822538Sesaxe return 6832538Sesaxe 6842538Sesaxe # elfdump(1) dumps the section headers with the section name 6852538Sesaxe # following "sh_name:", and the section type following "sh_type:" 6862538Sesaxe sections = hstring.split("Section Header") 6872538Sesaxe for sect in sections : 6882538Sesaxe datap = sect.find("sh_name:"); 6892538Sesaxe if datap == -1 : 6902538Sesaxe continue 6912538Sesaxe section = sect[datap:].split()[1] 6922538Sesaxe datap = sect.find("sh_type:"); 6932538Sesaxe if datap == -1 : 6942538Sesaxe error("Could not get type for sect: " + section + \ 6952538Sesaxe " in " + f) 6962538Sesaxe sh_type = sect[datap:].split()[2] 6972538Sesaxe header[section] = sh_type 6982538Sesaxe 6992538Sesaxe return header 7002538Sesaxe 7012538Sesaxe# 7022538Sesaxe# Extract data in the specified ELF section from the given file 7032538Sesaxe# 7042538Sesaxedef extract_elf_section(f, section) : 7052538Sesaxe 7062538Sesaxe data = commands.getoutput(dump_cmd + " -sn " + section + " " + f) 7072538Sesaxe 7082538Sesaxe if len(data) == 0 : 709*12887SVladimir.Kotal@Sun.COM error(dump_cmd + "yielded no data on section " + section + \ 710*12887SVladimir.Kotal@Sun.COM " of " + f) 711*12887SVladimir.Kotal@Sun.COM raise 7122538Sesaxe return 7132538Sesaxe 7142538Sesaxe # dump(1) displays the file name to start... 7152538Sesaxe # get past it to the data itself 7162538Sesaxe dbegin = data.find(":") + 1 7172538Sesaxe data = data[dbegin:]; 7182538Sesaxe 7192538Sesaxe return (data) 7202538Sesaxe 7212538Sesaxe# 7222538Sesaxe# Return a (hopefully meaningful) human readable set of diffs 7232538Sesaxe# for the specified ELF section between f1 and f2 7242538Sesaxe# 7252538Sesaxe# Depending on the section, various means for dumping and diffing 7262538Sesaxe# the data may be employed. 7272538Sesaxe# 7282538Sesaxetext_sections = [ '.text', '.init', '.fini' ] 7292538Sesaxedef diff_elf_section(f1, f2, section, sh_type) : 7302538Sesaxe 731*12887SVladimir.Kotal@Sun.COM t = threading.currentThread() 732*12887SVladimir.Kotal@Sun.COM tmpFile1 = tmpDir1 + os.path.basename(f1) + t.getName() 733*12887SVladimir.Kotal@Sun.COM tmpFile2 = tmpDir2 + os.path.basename(f2) + t.getName() 734*12887SVladimir.Kotal@Sun.COM 7352538Sesaxe if (sh_type == "SHT_RELA") : # sh_type == SHT_RELA 7362538Sesaxe cmd1 = elfdump_cmd + " -r " + f1 + " > " + tmpFile1 7372538Sesaxe cmd2 = elfdump_cmd + " -r " + f2 + " > " + tmpFile2 7382538Sesaxe elif (section == ".group") : 7392538Sesaxe cmd1 = elfdump_cmd + " -g " + f1 + " > " + tmpFile1 7402538Sesaxe cmd2 = elfdump_cmd + " -g " + f2 + " > " + tmpFile2 7412538Sesaxe elif (section == ".hash") : 7422538Sesaxe cmd1 = elfdump_cmd + " -h " + f1 + " > " + tmpFile1 7432538Sesaxe cmd2 = elfdump_cmd + " -h " + f2 + " > " + tmpFile2 7442538Sesaxe elif (section == ".dynamic") : 7452538Sesaxe cmd1 = elfdump_cmd + " -d " + f1 + " > " + tmpFile1 7462538Sesaxe cmd2 = elfdump_cmd + " -d " + f2 + " > " + tmpFile2 7472538Sesaxe elif (section == ".got") : 7482538Sesaxe cmd1 = elfdump_cmd + " -G " + f1 + " > " + tmpFile1 7492538Sesaxe cmd2 = elfdump_cmd + " -G " + f2 + " > " + tmpFile2 7502538Sesaxe elif (section == ".SUNW_cap") : 7512538Sesaxe cmd1 = elfdump_cmd + " -H " + f1 + " > " + tmpFile1 7522538Sesaxe cmd2 = elfdump_cmd + " -H " + f2 + " > " + tmpFile2 7532538Sesaxe elif (section == ".interp") : 7542538Sesaxe cmd1 = elfdump_cmd + " -i " + f1 + " > " + tmpFile1 7552538Sesaxe cmd2 = elfdump_cmd + " -i " + f2 + " > " + tmpFile2 7562538Sesaxe elif (section == ".symtab" or section == ".dynsym") : 757*12887SVladimir.Kotal@Sun.COM cmd1 = elfdump_cmd + " -s -N " + section + " " + f1 + \ 758*12887SVladimir.Kotal@Sun.COM " > " + tmpFile1 759*12887SVladimir.Kotal@Sun.COM cmd2 = elfdump_cmd + " -s -N " + section + " " + f2 + \ 760*12887SVladimir.Kotal@Sun.COM " > " + tmpFile2 7612538Sesaxe elif (section in text_sections) : 7622538Sesaxe # dis sometimes complains when it hits something it doesn't 7632538Sesaxe # know how to disassemble. Just ignore it, as the output 7642538Sesaxe # being generated here is human readable, and we've already 7652538Sesaxe # correctly flagged the difference. 7662538Sesaxe cmd1 = dis_cmd + " -t " + section + " " + f1 + \ 7672538Sesaxe " 2>/dev/null | grep -v disassembly > " + tmpFile1 7682538Sesaxe cmd2 = dis_cmd + " -t " + section + " " + f2 + \ 7692538Sesaxe " 2>/dev/null | grep -v disassembly > " + tmpFile2 7702538Sesaxe else : 7712538Sesaxe cmd1 = elfdump_cmd + " -w " + tmpFile1 + " -N " + \ 7722538Sesaxe section + " " + f1 7732538Sesaxe cmd2 = elfdump_cmd + " -w " + tmpFile2 + " -N " + \ 7742538Sesaxe section + " " + f2 7752538Sesaxe 7762538Sesaxe os.system(cmd1) 7772538Sesaxe os.system(cmd2) 7782538Sesaxe 7792538Sesaxe data = diffFileData(tmpFile1, tmpFile2) 78011300Srichlowe@richlowe.net 781*12887SVladimir.Kotal@Sun.COM # remove temp files as we no longer need them 782*12887SVladimir.Kotal@Sun.COM try: 783*12887SVladimir.Kotal@Sun.COM os.unlink(tmpFile1) 784*12887SVladimir.Kotal@Sun.COM except OSError, e: 785*12887SVladimir.Kotal@Sun.COM error("diff_elf_section: unlink failed %s" % e) 786*12887SVladimir.Kotal@Sun.COM try: 787*12887SVladimir.Kotal@Sun.COM os.unlink(tmpFile2) 788*12887SVladimir.Kotal@Sun.COM except OSError, e: 789*12887SVladimir.Kotal@Sun.COM error("diff_elf_section: unlink failed %s" % e) 790*12887SVladimir.Kotal@Sun.COM 7912538Sesaxe return (data) 7922538Sesaxe 7932538Sesaxe# 7942538Sesaxe# compare the relevant sections of two ELF binaries 7952538Sesaxe# and report any differences 7962538Sesaxe# 7972538Sesaxe# Returns: 1 if any differenes found 7982538Sesaxe# 0 if no differences found 7992538Sesaxe# -1 on error 8002538Sesaxe# 8012538Sesaxe 8022538Sesaxe# Sections deliberately not considered when comparing two ELF 8032538Sesaxe# binaries. Differences observed in these sections are not considered 8042538Sesaxe# significant where patch deliverable identification is concerned. 8052538Sesaxesections_to_skip = [ ".SUNW_signature", 8062538Sesaxe ".comment", 8072538Sesaxe ".SUNW_ctf", 8082538Sesaxe ".debug", 8092538Sesaxe ".plt", 8102538Sesaxe ".rela.bss", 8112538Sesaxe ".rela.plt", 8122538Sesaxe ".line", 8132538Sesaxe ".note", 8143002Sesaxe ".compcom", 8152538Sesaxe ] 8162538Sesaxe 8172538Sesaxesections_preferred = [ ".rodata.str1.8", 8182538Sesaxe ".rodata.str1.1", 8192538Sesaxe ".rodata", 8202538Sesaxe ".data1", 8212538Sesaxe ".data", 8222538Sesaxe ".text", 8232538Sesaxe ] 8242538Sesaxe 8252538Sesaxedef compareElfs(base, ptch, quiet) : 8262538Sesaxe 8272538Sesaxe global logging 8282538Sesaxe 829*12887SVladimir.Kotal@Sun.COM try: 830*12887SVladimir.Kotal@Sun.COM base_header = get_elfheader(base) 831*12887SVladimir.Kotal@Sun.COM except: 832*12887SVladimir.Kotal@Sun.COM return 8332538Sesaxe sections = base_header.keys() 8342538Sesaxe 835*12887SVladimir.Kotal@Sun.COM try: 836*12887SVladimir.Kotal@Sun.COM ptch_header = get_elfheader(ptch) 837*12887SVladimir.Kotal@Sun.COM except: 838*12887SVladimir.Kotal@Sun.COM return 8392538Sesaxe e2_only_sections = ptch_header.keys() 8402538Sesaxe 8412538Sesaxe e1_only_sections = [] 8422538Sesaxe 8432538Sesaxe fileName = fnFormat(base) 8442538Sesaxe 8452538Sesaxe # Derive the list of ELF sections found only in 8462538Sesaxe # either e1 or e2. 8472538Sesaxe for sect in sections : 8482538Sesaxe if not sect in e2_only_sections : 8492538Sesaxe e1_only_sections.append(sect) 8502538Sesaxe else : 8512538Sesaxe e2_only_sections.remove(sect) 8522538Sesaxe 8532538Sesaxe if len(e1_only_sections) > 0 : 8542538Sesaxe if quiet : 8552538Sesaxe return 1 8562538Sesaxe 857*12887SVladimir.Kotal@Sun.COM data = "" 858*12887SVladimir.Kotal@Sun.COM if logging : 859*12887SVladimir.Kotal@Sun.COM slist = "" 860*12887SVladimir.Kotal@Sun.COM for sect in e1_only_sections : 861*12887SVladimir.Kotal@Sun.COM slist = slist + sect + "\t" 862*12887SVladimir.Kotal@Sun.COM data = "ELF sections found in " + \ 863*12887SVladimir.Kotal@Sun.COM base + " but not in " + ptch + \ 864*12887SVladimir.Kotal@Sun.COM "\n\n" + slist 865*12887SVladimir.Kotal@Sun.COM 866*12887SVladimir.Kotal@Sun.COM difference(fileName, "ELF", data) 8672538Sesaxe return 1 868*12887SVladimir.Kotal@Sun.COM 8692538Sesaxe if len(e2_only_sections) > 0 : 8702538Sesaxe if quiet : 8712538Sesaxe return 1 872*12887SVladimir.Kotal@Sun.COM 873*12887SVladimir.Kotal@Sun.COM data = "" 874*12887SVladimir.Kotal@Sun.COM if logging : 875*12887SVladimir.Kotal@Sun.COM slist = "" 876*12887SVladimir.Kotal@Sun.COM for sect in e2_only_sections : 877*12887SVladimir.Kotal@Sun.COM slist = slist + sect + "\t" 878*12887SVladimir.Kotal@Sun.COM data = "ELF sections found in " + \ 879*12887SVladimir.Kotal@Sun.COM ptch + " but not in " + base + \ 880*12887SVladimir.Kotal@Sun.COM "\n\n" + slist 8812538Sesaxe 882*12887SVladimir.Kotal@Sun.COM difference(fileName, "ELF", data) 8832538Sesaxe return 1 8842538Sesaxe 8852538Sesaxe # Look for preferred sections, and put those at the 8862538Sesaxe # top of the list of sections to compare 8872538Sesaxe for psect in sections_preferred : 8882538Sesaxe if psect in sections : 8892538Sesaxe sections.remove(psect) 8902538Sesaxe sections.insert(0, psect) 8912538Sesaxe 8922538Sesaxe # Compare ELF sections 8932538Sesaxe first_section = True 8942538Sesaxe for sect in sections : 8952538Sesaxe 8962538Sesaxe if sect in sections_to_skip : 8972538Sesaxe continue 8982538Sesaxe 899*12887SVladimir.Kotal@Sun.COM try: 900*12887SVladimir.Kotal@Sun.COM s1 = extract_elf_section(base, sect); 901*12887SVladimir.Kotal@Sun.COM except: 902*12887SVladimir.Kotal@Sun.COM return 903*12887SVladimir.Kotal@Sun.COM 904*12887SVladimir.Kotal@Sun.COM try: 905*12887SVladimir.Kotal@Sun.COM s2 = extract_elf_section(ptch, sect); 906*12887SVladimir.Kotal@Sun.COM except: 907*12887SVladimir.Kotal@Sun.COM return 9082538Sesaxe 9092538Sesaxe if len(s1) != len (s2) or s1 != s2: 9102538Sesaxe if not quiet: 9112538Sesaxe sh_type = base_header[sect] 912*12887SVladimir.Kotal@Sun.COM data = diff_elf_section(base, ptch, \ 913*12887SVladimir.Kotal@Sun.COM sect, sh_type) 9142538Sesaxe 9152538Sesaxe # If all ELF sections are being reported, then 9162538Sesaxe # invoke difference() to flag the file name to 9172538Sesaxe # stdout only once. Any other section differences 9182538Sesaxe # should be logged to the results file directly 9192538Sesaxe if not first_section : 920*12887SVladimir.Kotal@Sun.COM log_difference(fileName, \ 921*12887SVladimir.Kotal@Sun.COM "ELF " + sect, data) 9222538Sesaxe else : 923*12887SVladimir.Kotal@Sun.COM difference(fileName, "ELF " + sect, \ 924*12887SVladimir.Kotal@Sun.COM data) 92511300Srichlowe@richlowe.net 9262538Sesaxe if not reportAllSects : 9272538Sesaxe return 1 9282538Sesaxe first_section = False 929*12887SVladimir.Kotal@Sun.COM 9302538Sesaxe return 0 93111300Srichlowe@richlowe.net 9322538Sesaxe##### 933*12887SVladimir.Kotal@Sun.COM# recursively remove 2 directories 934*12887SVladimir.Kotal@Sun.COM# 935*12887SVladimir.Kotal@Sun.COM# Used for removal of temporary directory strucures (ignores any errors). 936*12887SVladimir.Kotal@Sun.COM# 937*12887SVladimir.Kotal@Sun.COMdef clearTmpDirs(dir1, dir2) : 938*12887SVladimir.Kotal@Sun.COM 939*12887SVladimir.Kotal@Sun.COM if os.path.isdir(dir1) > 0 : 940*12887SVladimir.Kotal@Sun.COM shutil.rmtree(dir1, True) 941*12887SVladimir.Kotal@Sun.COM 942*12887SVladimir.Kotal@Sun.COM if os.path.isdir(dir2) > 0 : 943*12887SVladimir.Kotal@Sun.COM shutil.rmtree(dir2, True) 944*12887SVladimir.Kotal@Sun.COM 945*12887SVladimir.Kotal@Sun.COM 946*12887SVladimir.Kotal@Sun.COM##### 9472538Sesaxe# Archive object comparison 9482538Sesaxe# 9492538Sesaxe# Returns 1 if difference detected 9502538Sesaxe# 0 if no difference detected 9512538Sesaxe# -1 on error 9522538Sesaxe# 9532538Sesaxedef compareArchives(base, ptch, fileType) : 9542538Sesaxe 9552538Sesaxe fileName = fnFormat(base) 956*12887SVladimir.Kotal@Sun.COM t = threading.currentThread() 957*12887SVladimir.Kotal@Sun.COM ArchTmpDir1 = tmpDir1 + os.path.basename(base) + t.getName() 958*12887SVladimir.Kotal@Sun.COM ArchTmpDir2 = tmpDir2 + os.path.basename(base) + t.getName() 9592538Sesaxe 9602538Sesaxe # 9612538Sesaxe # Be optimistic and first try a straight file compare 9622538Sesaxe # as it will allow us to finish up quickly. 963*12887SVladimir.Kotal@Sun.COM # 9642538Sesaxe if compareBasic(base, ptch, True, fileType) == 0 : 9652538Sesaxe return 0 9662538Sesaxe 967*12887SVladimir.Kotal@Sun.COM try: 968*12887SVladimir.Kotal@Sun.COM os.makedirs(ArchTmpDir1) 969*12887SVladimir.Kotal@Sun.COM except OSError, e: 970*12887SVladimir.Kotal@Sun.COM error("compareArchives: makedir failed %s" % e) 971*12887SVladimir.Kotal@Sun.COM return -1 972*12887SVladimir.Kotal@Sun.COM try: 973*12887SVladimir.Kotal@Sun.COM os.makedirs(ArchTmpDir2) 974*12887SVladimir.Kotal@Sun.COM except OSError, e: 975*12887SVladimir.Kotal@Sun.COM error("compareArchives: makedir failed %s" % e) 976*12887SVladimir.Kotal@Sun.COM return -1 977*12887SVladimir.Kotal@Sun.COM 9782538Sesaxe # copy over the objects to the temp areas, and 9792538Sesaxe # unpack them 980*12887SVladimir.Kotal@Sun.COM baseCmd = "cp -fp " + base + " " + ArchTmpDir1 9812538Sesaxe status, output = commands.getstatusoutput(baseCmd) 9822538Sesaxe if status != 0 : 9832538Sesaxe error(baseCmd + " failed: " + output) 984*12887SVladimir.Kotal@Sun.COM clearTmpDirs(ArchTmpDir1, ArchTmpDir2) 9852538Sesaxe return -1 9862538Sesaxe 987*12887SVladimir.Kotal@Sun.COM ptchCmd = "cp -fp " + ptch + " " + ArchTmpDir2 9882538Sesaxe status, output = commands.getstatusoutput(ptchCmd) 9892538Sesaxe if status != 0 : 9902538Sesaxe error(ptchCmd + " failed: " + output) 991*12887SVladimir.Kotal@Sun.COM clearTmpDirs(ArchTmpDir1, ArchTmpDir2) 9922538Sesaxe return -1 9932538Sesaxe 9942538Sesaxe bname = string.split(fileName, '/')[-1] 9952538Sesaxe if fileType == "Java Archive" : 996*12887SVladimir.Kotal@Sun.COM baseCmd = "cd " + ArchTmpDir1 + "; " + "jar xf " + bname + \ 9972538Sesaxe "; rm -f " + bname + " META-INF/MANIFEST.MF" 998*12887SVladimir.Kotal@Sun.COM ptchCmd = "cd " + ArchTmpDir2 + "; " + "jar xf " + bname + \ 9992538Sesaxe "; rm -f " + bname + " META-INF/MANIFEST.MF" 10002538Sesaxe elif fileType == "ELF Object Archive" : 1001*12887SVladimir.Kotal@Sun.COM baseCmd = "cd " + ArchTmpDir1 + "; " + "/usr/ccs/bin/ar x " + \ 10022538Sesaxe bname + "; rm -f " + bname 1003*12887SVladimir.Kotal@Sun.COM ptchCmd = "cd " + ArchTmpDir2 + "; " + "/usr/ccs/bin/ar x " + \ 10042538Sesaxe bname + "; rm -f " + bname 10052538Sesaxe else : 10062538Sesaxe error("unexpected file type: " + fileType) 1007*12887SVladimir.Kotal@Sun.COM clearTmpDirs(ArchTmpDir1, ArchTmpDir2) 10082538Sesaxe return -1 10092538Sesaxe 10102538Sesaxe os.system(baseCmd) 10112538Sesaxe os.system(ptchCmd) 10122538Sesaxe 1013*12887SVladimir.Kotal@Sun.COM baseFlist = list(findFiles(ArchTmpDir1)) 1014*12887SVladimir.Kotal@Sun.COM ptchFlist = list(findFiles(ArchTmpDir2)) 10152538Sesaxe 10162538Sesaxe # Trim leading path off base/ptch file lists 10172538Sesaxe flist = [] 10182538Sesaxe for fn in baseFlist : 1019*12887SVladimir.Kotal@Sun.COM flist.append(str_prefix_trunc(fn, ArchTmpDir1)) 10202538Sesaxe baseFlist = flist 10212538Sesaxe 10222538Sesaxe flist = [] 10232538Sesaxe for fn in ptchFlist : 1024*12887SVladimir.Kotal@Sun.COM flist.append(str_prefix_trunc(fn, ArchTmpDir2)) 10252538Sesaxe ptchFlist = flist 10262538Sesaxe 10272538Sesaxe for fn in ptchFlist : 10282538Sesaxe if not fn in baseFlist : 10292538Sesaxe difference(fileName, fileType, \ 10302538Sesaxe fn + " added to " + fileName) 1031*12887SVladimir.Kotal@Sun.COM clearTmpDirs(ArchTmpDir1, ArchTmpDir2) 10322538Sesaxe return 1 10332538Sesaxe 10342538Sesaxe for fn in baseFlist : 10352538Sesaxe if not fn in ptchFlist : 10362538Sesaxe difference(fileName, fileType, \ 10372538Sesaxe fn + " removed from " + fileName) 1038*12887SVladimir.Kotal@Sun.COM clearTmpDirs(ArchTmpDir1, ArchTmpDir2) 10392538Sesaxe return 1 10402538Sesaxe 1041*12887SVladimir.Kotal@Sun.COM differs = compareOneFile((ArchTmpDir1 + fn), \ 1042*12887SVladimir.Kotal@Sun.COM (ArchTmpDir2 + fn), True) 10432538Sesaxe if differs : 10442538Sesaxe difference(fileName, fileType, \ 10452538Sesaxe fn + " in " + fileName + " differs") 1046*12887SVladimir.Kotal@Sun.COM clearTmpDirs(ArchTmpDir1, ArchTmpDir2) 10472538Sesaxe return 1 1048*12887SVladimir.Kotal@Sun.COM 1049*12887SVladimir.Kotal@Sun.COM clearTmpDirs(ArchTmpDir1, ArchTmpDir2) 10502538Sesaxe return 0 10512538Sesaxe 10522538Sesaxe##### 10532538Sesaxe# (Basic) file comparison 10542538Sesaxe# 10552538Sesaxe# There's some special case code here for Javadoc HTML files 105611300Srichlowe@richlowe.net# 10572538Sesaxe# Returns 1 if difference detected 10582538Sesaxe# 0 if no difference detected 10592538Sesaxe# -1 on error 10602538Sesaxe# 10612538Sesaxedef compareBasic(base, ptch, quiet, fileType) : 10622538Sesaxe 10632538Sesaxe fileName = fnFormat(base); 10642538Sesaxe 10652538Sesaxe if quiet and os.stat(base)[ST_SIZE] != os.stat(ptch)[ST_SIZE] : 10662538Sesaxe return 1 10672538Sesaxe 10682538Sesaxe try: 10692538Sesaxe baseFile = open(base) 10702538Sesaxe except: 10712538Sesaxe error("could not open " + base) 10722538Sesaxe return -1 10732538Sesaxe try: 10742538Sesaxe ptchFile = open(ptch) 10752538Sesaxe except: 10762538Sesaxe error("could not open " + ptch) 10772538Sesaxe return -1 10782538Sesaxe 10792538Sesaxe baseData = baseFile.read() 10802538Sesaxe ptchData = ptchFile.read() 10812538Sesaxe 10822538Sesaxe baseFile.close() 10832538Sesaxe ptchFile.close() 10842538Sesaxe 10852538Sesaxe needToSnip = False 10862538Sesaxe if fileType == "HTML" : 10872538Sesaxe needToSnip = True 10882538Sesaxe toSnipBeginStr = "<!-- Generated by javadoc" 10892538Sesaxe toSnipEndStr = "-->\n" 10902538Sesaxe 10912538Sesaxe if needToSnip : 10922538Sesaxe toSnipBegin = string.find(baseData, toSnipBeginStr) 10932538Sesaxe if toSnipBegin != -1 : 10942538Sesaxe toSnipEnd = string.find(baseData[toSnipBegin:], \ 10952538Sesaxe toSnipEndStr) + \ 10962538Sesaxe len(toSnipEndStr) 10972538Sesaxe baseData = baseData[:toSnipBegin] + \ 10982538Sesaxe baseData[toSnipBegin + toSnipEnd:] 10992538Sesaxe ptchData = ptchData[:toSnipBegin] + \ 11002538Sesaxe ptchData[toSnipBegin + toSnipEnd:] 11012538Sesaxe 11022538Sesaxe if quiet : 11032538Sesaxe if baseData != ptchData : 11042538Sesaxe return 1 11052538Sesaxe else : 11062538Sesaxe if len(baseData) != len(ptchData) or baseData != ptchData : 1107*12887SVladimir.Kotal@Sun.COM diffs = diffData(base, ptch, baseData, ptchData) 11082538Sesaxe difference(fileName, fileType, diffs) 11092538Sesaxe return 1 11102538Sesaxe return 0 11112538Sesaxe 11122538Sesaxe 11132538Sesaxe##### 11142538Sesaxe# Compare two objects by producing a data dump from 11152538Sesaxe# each object, and then comparing the dump data 11162538Sesaxe# 11172538Sesaxe# Returns: 1 if a difference is detected 11182538Sesaxe# 0 if no difference detected 11192538Sesaxe# -1 upon error 11202538Sesaxe# 11212538Sesaxedef compareByDumping(base, ptch, quiet, fileType) : 11222538Sesaxe 11232538Sesaxe fileName = fnFormat(base); 1124*12887SVladimir.Kotal@Sun.COM t = threading.currentThread() 1125*12887SVladimir.Kotal@Sun.COM tmpFile1 = tmpDir1 + os.path.basename(base) + t.getName() 1126*12887SVladimir.Kotal@Sun.COM tmpFile2 = tmpDir2 + os.path.basename(ptch) + t.getName() 11272538Sesaxe 11282538Sesaxe if fileType == "Lint Library" : 11292538Sesaxe baseCmd = lintdump_cmd + " -ir " + base + \ 1130*12887SVladimir.Kotal@Sun.COM " | egrep -v '(LINTOBJ|LINTMOD):'" + \ 1131*12887SVladimir.Kotal@Sun.COM " | grep -v PASS[1-3]:" + \ 1132*12887SVladimir.Kotal@Sun.COM " > " + tmpFile1 11332538Sesaxe ptchCmd = lintdump_cmd + " -ir " + ptch + \ 1134*12887SVladimir.Kotal@Sun.COM " | egrep -v '(LINTOBJ|LINTMOD):'" + \ 1135*12887SVladimir.Kotal@Sun.COM " | grep -v PASS[1-3]:" + \ 1136*12887SVladimir.Kotal@Sun.COM " > " + tmpFile2 11372538Sesaxe elif fileType == "Sqlite Database" : 11382538Sesaxe baseCmd = "echo .dump | " + sqlite_cmd + base + " > " + \ 11392538Sesaxe tmpFile1 11402538Sesaxe ptchCmd = "echo .dump | " + sqlite_cmd + ptch + " > " + \ 11412538Sesaxe tmpFile2 1142*12887SVladimir.Kotal@Sun.COM 11432538Sesaxe os.system(baseCmd) 11442538Sesaxe os.system(ptchCmd) 11452538Sesaxe 11462538Sesaxe try: 11472538Sesaxe baseFile = open(tmpFile1) 11482538Sesaxe except: 11492538Sesaxe error("could not open: " + tmpFile1) 1150*12887SVladimir.Kotal@Sun.COM return 11512538Sesaxe try: 11522538Sesaxe ptchFile = open(tmpFile2) 11532538Sesaxe except: 11542538Sesaxe error("could not open: " + tmpFile2) 1155*12887SVladimir.Kotal@Sun.COM return 11562538Sesaxe 11572538Sesaxe baseData = baseFile.read() 11582538Sesaxe ptchData = ptchFile.read() 11592538Sesaxe 11602538Sesaxe baseFile.close() 11612538Sesaxe ptchFile.close() 11622538Sesaxe 11632538Sesaxe if len(baseData) != len(ptchData) or baseData != ptchData : 11642538Sesaxe if not quiet : 11652538Sesaxe data = diffFileData(tmpFile1, tmpFile2); 1166*12887SVladimir.Kotal@Sun.COM try: 1167*12887SVladimir.Kotal@Sun.COM os.unlink(tmpFile1) 1168*12887SVladimir.Kotal@Sun.COM except OSError, e: 1169*12887SVladimir.Kotal@Sun.COM error("compareByDumping: unlink failed %s" % e) 1170*12887SVladimir.Kotal@Sun.COM try: 1171*12887SVladimir.Kotal@Sun.COM os.unlink(tmpFile2) 1172*12887SVladimir.Kotal@Sun.COM except OSError, e: 1173*12887SVladimir.Kotal@Sun.COM error("compareByDumping: unlink failed %s" % e) 11742538Sesaxe difference(fileName, fileType, data) 11752538Sesaxe return 1 1176*12887SVladimir.Kotal@Sun.COM 1177*12887SVladimir.Kotal@Sun.COM # Remove the temporary files now. 1178*12887SVladimir.Kotal@Sun.COM try: 1179*12887SVladimir.Kotal@Sun.COM os.unlink(tmpFile1) 1180*12887SVladimir.Kotal@Sun.COM except OSError, e: 1181*12887SVladimir.Kotal@Sun.COM error("compareByDumping: unlink failed %s" % e) 1182*12887SVladimir.Kotal@Sun.COM try: 1183*12887SVladimir.Kotal@Sun.COM os.unlink(tmpFile2) 1184*12887SVladimir.Kotal@Sun.COM except OSError, e: 1185*12887SVladimir.Kotal@Sun.COM error("compareByDumping: unlink failed %s" % e) 1186*12887SVladimir.Kotal@Sun.COM 11872538Sesaxe return 0 11882538Sesaxe 11892538Sesaxe##### 1190*12887SVladimir.Kotal@Sun.COM# 1191*12887SVladimir.Kotal@Sun.COM# SIGINT signal handler. Changes thread control variable to tell the threads 1192*12887SVladimir.Kotal@Sun.COM# to finish their current job and exit. 11935298Srotondo# 1194*12887SVladimir.Kotal@Sun.COMdef discontinue_processing(signl, frme): 1195*12887SVladimir.Kotal@Sun.COM global keep_processing 11965298Srotondo 1197*12887SVladimir.Kotal@Sun.COM print >> sys.stderr, "Caught Ctrl-C, stopping the threads" 1198*12887SVladimir.Kotal@Sun.COM keep_processing = False 1199*12887SVladimir.Kotal@Sun.COM 1200*12887SVladimir.Kotal@Sun.COM return 0 12015298Srotondo 1202*12887SVladimir.Kotal@Sun.COM##### 1203*12887SVladimir.Kotal@Sun.COM# 1204*12887SVladimir.Kotal@Sun.COM# worker thread for changedFiles processing 1205*12887SVladimir.Kotal@Sun.COM# 1206*12887SVladimir.Kotal@Sun.COMclass workerThread(threading.Thread) : 1207*12887SVladimir.Kotal@Sun.COM def run(self): 1208*12887SVladimir.Kotal@Sun.COM global wset_lock 1209*12887SVladimir.Kotal@Sun.COM global changedFiles 1210*12887SVladimir.Kotal@Sun.COM global baseRoot 1211*12887SVladimir.Kotal@Sun.COM global ptchRoot 1212*12887SVladimir.Kotal@Sun.COM global keep_processing 12135298Srotondo 1214*12887SVladimir.Kotal@Sun.COM while (keep_processing) : 1215*12887SVladimir.Kotal@Sun.COM # grab the lock to changedFiles and remove one member 1216*12887SVladimir.Kotal@Sun.COM # and process it 1217*12887SVladimir.Kotal@Sun.COM wset_lock.acquire() 1218*12887SVladimir.Kotal@Sun.COM try : 1219*12887SVladimir.Kotal@Sun.COM fn = changedFiles.pop() 1220*12887SVladimir.Kotal@Sun.COM except IndexError : 1221*12887SVladimir.Kotal@Sun.COM # there is nothing more to do 1222*12887SVladimir.Kotal@Sun.COM wset_lock.release() 1223*12887SVladimir.Kotal@Sun.COM return 1224*12887SVladimir.Kotal@Sun.COM wset_lock.release() 12255298Srotondo 1226*12887SVladimir.Kotal@Sun.COM base = baseRoot + fn 1227*12887SVladimir.Kotal@Sun.COM ptch = ptchRoot + fn 1228*12887SVladimir.Kotal@Sun.COM 1229*12887SVladimir.Kotal@Sun.COM compareOneFile(base, ptch, False) 1230*12887SVladimir.Kotal@Sun.COM 12315298Srotondo 12325298Srotondo##### 12332538Sesaxe# Compare two objects. Detect type changes. 12342538Sesaxe# Vector off to the appropriate type specific 12352538Sesaxe# compare routine based on the type. 123611300Srichlowe@richlowe.net# 12372538Sesaxedef compareOneFile(base, ptch, quiet) : 12382538Sesaxe 12392538Sesaxe # Verify the file types. 12402538Sesaxe # If they are different, indicate this and move on 12412538Sesaxe btype = getTheFileType(base) 12422538Sesaxe ptype = getTheFileType(ptch) 12432538Sesaxe 12445298Srotondo if btype == 'Error' or ptype == 'Error' : 12455298Srotondo return -1 12465298Srotondo 12472538Sesaxe fileName = fnFormat(base) 12482538Sesaxe 12492538Sesaxe if (btype != ptype) : 12505298Srotondo if not quiet : 12515298Srotondo difference(fileName, "file type", btype + " to " + ptype) 12522538Sesaxe return 1 12532538Sesaxe else : 12542538Sesaxe fileType = btype 12552538Sesaxe 12562538Sesaxe if (fileType == 'ELF') : 12572538Sesaxe return compareElfs(base, ptch, quiet) 12582538Sesaxe 12592538Sesaxe elif (fileType == 'Java Archive' or fileType == 'ELF Object Archive') : 12602538Sesaxe return compareArchives(base, ptch, fileType) 12612538Sesaxe 12622538Sesaxe elif (fileType == 'HTML') : 12632538Sesaxe return compareBasic(base, ptch, quiet, fileType) 12642538Sesaxe 12652538Sesaxe elif ( fileType == 'Lint Library' ) : 12662538Sesaxe return compareByDumping(base, ptch, quiet, fileType) 12672538Sesaxe 12682538Sesaxe elif ( fileType == 'Sqlite Database' ) : 12692538Sesaxe return compareByDumping(base, ptch, quiet, fileType) 12705298Srotondo 12712538Sesaxe else : 12722538Sesaxe # it has to be some variety of text file 12732538Sesaxe return compareBasic(base, ptch, quiet, fileType) 12742538Sesaxe 12752538Sesaxe# Cleanup and self-terminate 12762538Sesaxedef cleanup(ret) : 12772538Sesaxe 1278*12887SVladimir.Kotal@Sun.COM debug("Performing cleanup (" + str(ret) + ")") 1279*12887SVladimir.Kotal@Sun.COM if os.path.isdir(tmpDir1) > 0 : 1280*12887SVladimir.Kotal@Sun.COM shutil.rmtree(tmpDir1) 1281*12887SVladimir.Kotal@Sun.COM 1282*12887SVladimir.Kotal@Sun.COM if os.path.isdir(tmpDir2) > 0 : 1283*12887SVladimir.Kotal@Sun.COM shutil.rmtree(tmpDir2) 1284*12887SVladimir.Kotal@Sun.COM 12852538Sesaxe if logging : 12862538Sesaxe log.close() 128711300Srichlowe@richlowe.net 12882538Sesaxe sys.exit(ret) 12892538Sesaxe 12902538Sesaxedef main() : 12912538Sesaxe 12922538Sesaxe # Log file handle 12932538Sesaxe global log 12942538Sesaxe 12952538Sesaxe # Globals relating to command line options 12962538Sesaxe global logging, vdiffs, reportAllSects 12972538Sesaxe 12982538Sesaxe # Named temporary files / directories 1299*12887SVladimir.Kotal@Sun.COM global tmpDir1, tmpDir2 13002538Sesaxe 13012538Sesaxe # Command paths 13022538Sesaxe global lintdump_cmd, elfdump_cmd, dump_cmd, dis_cmd, od_cmd, diff_cmd, sqlite_cmd 13032538Sesaxe 13042538Sesaxe # Default search path 13052538Sesaxe global wsdiff_path 13062538Sesaxe 13072538Sesaxe # Essentially "uname -p" 13082538Sesaxe global arch 13092538Sesaxe 1310*12887SVladimir.Kotal@Sun.COM # changed files for worker thread processing 1311*12887SVladimir.Kotal@Sun.COM global changedFiles 1312*12887SVladimir.Kotal@Sun.COM global baseRoot 1313*12887SVladimir.Kotal@Sun.COM global ptchRoot 1314*12887SVladimir.Kotal@Sun.COM 1315*12887SVladimir.Kotal@Sun.COM # Sort the list of files from a temporary file 1316*12887SVladimir.Kotal@Sun.COM global sorted 1317*12887SVladimir.Kotal@Sun.COM global differentFiles 1318*12887SVladimir.Kotal@Sun.COM 1319*12887SVladimir.Kotal@Sun.COM # Debugging indicator 1320*12887SVladimir.Kotal@Sun.COM global debugon 1321*12887SVladimir.Kotal@Sun.COM 13222538Sesaxe # Some globals need to be initialized 1323*12887SVladimir.Kotal@Sun.COM debugon = logging = vdiffs = reportAllSects = sorted = False 13242538Sesaxe 13252538Sesaxe 13262538Sesaxe # Process command line arguments 13272538Sesaxe # Return values are returned from args() in alpha order 13282538Sesaxe # (Yes, python functions can return multiple values (ewww)) 13292538Sesaxe # Note that args() also set the globals: 13302538Sesaxe # logging to True if verbose logging (to a file) was enabled 13312538Sesaxe # vdiffs to True if logged differences aren't to be truncated 13322538Sesaxe # reportAllSects to True if all ELF section differences are to be reported 13332538Sesaxe # 13342538Sesaxe baseRoot, fileNamesFile, localTools, ptchRoot, results = args() 13352538Sesaxe 13362538Sesaxe # 13372538Sesaxe # Set up the results/log file 13382538Sesaxe # 13392538Sesaxe if logging : 13402538Sesaxe try: 13412538Sesaxe log = open(results, "w") 13422538Sesaxe except: 13432538Sesaxe logging = False 13442538Sesaxe error("failed to open log file: " + log) 13452538Sesaxe sys.exit(1) 13462538Sesaxe 13472538Sesaxe dateTimeStr= "# %d/%d/%d at %d:%d:%d" % time.localtime()[:6] 13482538Sesaxe v_info("# This file was produced by wsdiff") 13492538Sesaxe v_info(dateTimeStr) 13502538Sesaxe 1351*12887SVladimir.Kotal@Sun.COM # Changed files (used only for the sorted case) 1352*12887SVladimir.Kotal@Sun.COM if sorted : 1353*12887SVladimir.Kotal@Sun.COM differentFiles = [] 1354*12887SVladimir.Kotal@Sun.COM 1355*12887SVladimir.Kotal@Sun.COM # 13562538Sesaxe # Build paths to the tools required tools 13572538Sesaxe # 13582538Sesaxe # Try to look for tools in $SRC/tools if the "-t" option 13592538Sesaxe # was specified 13602538Sesaxe # 13612538Sesaxe arch = commands.getoutput("uname -p") 13622538Sesaxe if localTools : 13632538Sesaxe try: 13642538Sesaxe src = os.environ['SRC'] 13652538Sesaxe except: 13662538Sesaxe error("-t specified, but $SRC not set. Cannot find $SRC/tools") 13672538Sesaxe src = "" 13682538Sesaxe if len(src) > 0 : 13692538Sesaxe wsdiff_path.insert(0, src + "/tools/proto/opt/onbld/bin") 13702538Sesaxe 13712538Sesaxe lintdump_cmd = find_tool("lintdump") 13722538Sesaxe elfdump_cmd = find_tool("elfdump") 13732538Sesaxe dump_cmd = find_tool("dump") 13742538Sesaxe od_cmd = find_tool("od") 13752538Sesaxe dis_cmd = find_tool("dis") 13762538Sesaxe diff_cmd = find_tool("diff") 13772538Sesaxe sqlite_cmd = find_tool("sqlite") 13782538Sesaxe 13792538Sesaxe # 1380*12887SVladimir.Kotal@Sun.COM # Set resource limit for number of open files as high as possible. 1381*12887SVladimir.Kotal@Sun.COM # This might get handy with big number of threads. 1382*12887SVladimir.Kotal@Sun.COM # 1383*12887SVladimir.Kotal@Sun.COM (nofile_soft, nofile_hard) = resource.getrlimit(resource.RLIMIT_NOFILE) 1384*12887SVladimir.Kotal@Sun.COM try: 1385*12887SVladimir.Kotal@Sun.COM resource.setrlimit(resource.RLIMIT_NOFILE, 1386*12887SVladimir.Kotal@Sun.COM (nofile_hard, nofile_hard)) 1387*12887SVladimir.Kotal@Sun.COM except: 1388*12887SVladimir.Kotal@Sun.COM error("cannot set resource limits for number of open files") 1389*12887SVladimir.Kotal@Sun.COM sys.exit(1) 1390*12887SVladimir.Kotal@Sun.COM 1391*12887SVladimir.Kotal@Sun.COM # 13922538Sesaxe # validate the base and patch paths 13932538Sesaxe # 13942538Sesaxe if baseRoot[-1] != '/' : 13952538Sesaxe baseRoot += '/' 13962538Sesaxe 13972538Sesaxe if ptchRoot[-1] != '/' : 13982538Sesaxe ptchRoot += '/' 13992538Sesaxe 14002538Sesaxe if not os.path.exists(baseRoot) : 14012538Sesaxe error("old proto area: " + baseRoot + " does not exist") 14022538Sesaxe sys.exit(1) 14032538Sesaxe 14042538Sesaxe if not os.path.exists(ptchRoot) : 14052538Sesaxe error("new proto area: " + ptchRoot + \ 14062538Sesaxe " does not exist") 14072538Sesaxe sys.exit(1) 14082538Sesaxe 14092538Sesaxe # 14102538Sesaxe # log some information identifying the run 14112538Sesaxe # 14122538Sesaxe v_info("Old proto area: " + baseRoot) 14132538Sesaxe v_info("New proto area: " + ptchRoot) 14142538Sesaxe v_info("Results file: " + results + "\n") 14152538Sesaxe 141611300Srichlowe@richlowe.net # 14172538Sesaxe # Set up the temporary directories / files 14182538Sesaxe # Could use python's tmpdir routines, but these should 14192538Sesaxe # be easier to identify / keep around for debugging 14202538Sesaxe pid = os.getpid() 14212538Sesaxe tmpDir1 = "/tmp/wsdiff_tmp1_" + str(pid) + "/" 14222538Sesaxe tmpDir2 = "/tmp/wsdiff_tmp2_" + str(pid) + "/" 1423*12887SVladimir.Kotal@Sun.COM try: 14242538Sesaxe os.makedirs(tmpDir1) 1425*12887SVladimir.Kotal@Sun.COM except OSError, e: 1426*12887SVladimir.Kotal@Sun.COM error("main: makedir failed %s" % e) 1427*12887SVladimir.Kotal@Sun.COM try: 14282538Sesaxe os.makedirs(tmpDir2) 1429*12887SVladimir.Kotal@Sun.COM except OSError, e: 1430*12887SVladimir.Kotal@Sun.COM error("main: makedir failed %s" % e) 14312538Sesaxe 14322538Sesaxe # Derive a catalog of new, deleted, and to-be-compared objects 14332538Sesaxe # either from the specified base and patch proto areas, or from 14342538Sesaxe # from an input file list 14352538Sesaxe newOrDeleted = False 14362538Sesaxe 14372538Sesaxe if fileNamesFile != "" : 14382538Sesaxe changedFiles, newFiles, deletedFiles = \ 14392538Sesaxe flistCatalog(baseRoot, ptchRoot, fileNamesFile) 14402538Sesaxe else : 1441*12887SVladimir.Kotal@Sun.COM changedFiles, newFiles, deletedFiles = \ 1442*12887SVladimir.Kotal@Sun.COM protoCatalog(baseRoot, ptchRoot) 14432538Sesaxe 14442538Sesaxe if len(newFiles) > 0 : 14452538Sesaxe newOrDeleted = True 14462538Sesaxe info("\nNew objects found: ") 14472538Sesaxe 1448*12887SVladimir.Kotal@Sun.COM if sorted : 1449*12887SVladimir.Kotal@Sun.COM newFiles.sort() 14502538Sesaxe for fn in newFiles : 14512538Sesaxe info(fnFormat(fn)) 14522538Sesaxe 14532538Sesaxe if len(deletedFiles) > 0 : 14542538Sesaxe newOrDeleted = True 14552538Sesaxe info("\nObjects removed: ") 14562538Sesaxe 1457*12887SVladimir.Kotal@Sun.COM if sorted : 1458*12887SVladimir.Kotal@Sun.COM deletedFiles.sort() 14592538Sesaxe for fn in deletedFiles : 14602538Sesaxe info(fnFormat(fn)) 14612538Sesaxe 14622538Sesaxe if newOrDeleted : 1463*12887SVladimir.Kotal@Sun.COM info("\nChanged objects: ") 1464*12887SVladimir.Kotal@Sun.COM if sorted : 1465*12887SVladimir.Kotal@Sun.COM debug("The list will appear after the processing is done") 14662538Sesaxe 14672538Sesaxe # Here's where all the heavy lifting happens 14682538Sesaxe # Perform a comparison on each object appearing in 14692538Sesaxe # both proto areas. compareOneFile will examine the 14702538Sesaxe # file types of each object, and will vector off to 14712538Sesaxe # the appropriate comparison routine, where the compare 14722538Sesaxe # will happen, and any differences will be reported / logged 1473*12887SVladimir.Kotal@Sun.COM 1474*12887SVladimir.Kotal@Sun.COM # determine maximum number of worker threads by using 1475*12887SVladimir.Kotal@Sun.COM # DMAKE_MAX_JOBS environment variable set by nightly(1) 1476*12887SVladimir.Kotal@Sun.COM # or get number of CPUs in the system 1477*12887SVladimir.Kotal@Sun.COM try: 1478*12887SVladimir.Kotal@Sun.COM max_threads = int(os.environ['DMAKE_MAX_JOBS']) 1479*12887SVladimir.Kotal@Sun.COM except: 1480*12887SVladimir.Kotal@Sun.COM max_threads = os.sysconf("SC_NPROCESSORS_ONLN") 1481*12887SVladimir.Kotal@Sun.COM # If we cannot get number of online CPUs in the system 1482*12887SVladimir.Kotal@Sun.COM # run unparallelized otherwise bump the number up 20% 1483*12887SVladimir.Kotal@Sun.COM # to achieve best results. 1484*12887SVladimir.Kotal@Sun.COM if max_threads == -1 : 1485*12887SVladimir.Kotal@Sun.COM max_threads = 1 1486*12887SVladimir.Kotal@Sun.COM else : 1487*12887SVladimir.Kotal@Sun.COM max_threads += max_threads/5 1488*12887SVladimir.Kotal@Sun.COM 1489*12887SVladimir.Kotal@Sun.COM # Set signal handler to attempt graceful exit 1490*12887SVladimir.Kotal@Sun.COM debug("Setting signal handler") 1491*12887SVladimir.Kotal@Sun.COM signal.signal( signal.SIGINT, discontinue_processing ) 149211300Srichlowe@richlowe.net 1493*12887SVladimir.Kotal@Sun.COM # Create and unleash the threads 1494*12887SVladimir.Kotal@Sun.COM # Only at most max_threads must be running at any moment 1495*12887SVladimir.Kotal@Sun.COM mythreads = [] 1496*12887SVladimir.Kotal@Sun.COM debug("Spawning " + str(max_threads) + " threads"); 1497*12887SVladimir.Kotal@Sun.COM for i in range(max_threads) : 1498*12887SVladimir.Kotal@Sun.COM thread = workerThread() 1499*12887SVladimir.Kotal@Sun.COM mythreads.append(thread) 1500*12887SVladimir.Kotal@Sun.COM mythreads[i].start() 1501*12887SVladimir.Kotal@Sun.COM 1502*12887SVladimir.Kotal@Sun.COM # Wait for the threads to finish and do cleanup if interrupted 1503*12887SVladimir.Kotal@Sun.COM debug("Waiting for the threads to finish") 1504*12887SVladimir.Kotal@Sun.COM while True: 1505*12887SVladimir.Kotal@Sun.COM if not True in [thread.isAlive() for thread in mythreads]: 1506*12887SVladimir.Kotal@Sun.COM break 1507*12887SVladimir.Kotal@Sun.COM else: 1508*12887SVladimir.Kotal@Sun.COM # Some threads are still going 1509*12887SVladimir.Kotal@Sun.COM time.sleep(1) 1510*12887SVladimir.Kotal@Sun.COM 1511*12887SVladimir.Kotal@Sun.COM # Interrupted by SIGINT 1512*12887SVladimir.Kotal@Sun.COM if keep_processing == False : 1513*12887SVladimir.Kotal@Sun.COM cleanup(1) 1514*12887SVladimir.Kotal@Sun.COM 1515*12887SVladimir.Kotal@Sun.COM # If the list of differences was sorted it is stored in an array 1516*12887SVladimir.Kotal@Sun.COM if sorted : 1517*12887SVladimir.Kotal@Sun.COM differentFiles.sort() 1518*12887SVladimir.Kotal@Sun.COM for f in differentFiles : 1519*12887SVladimir.Kotal@Sun.COM info(fnFormat(f)) 15202538Sesaxe 15212538Sesaxe # We're done, cleanup. 15222538Sesaxe cleanup(0) 15232538Sesaxe 15242538Sesaxeif __name__ == '__main__' : 15252538Sesaxe try: 15262538Sesaxe main() 15272538Sesaxe except KeyboardInterrupt : 15282538Sesaxe cleanup(1); 15292538Sesaxe 15302538Sesaxe 1531