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