Back to index

enigmail  1.4.3
Classes | Functions | Variables
build.automationutils Namespace Reference

Classes

class  ZipFileReader
class  ShutdownLeakLogger

Functions

def isURL
def addCommonOptions
def checkForCrashes
def getFullPath
def searchPath
def getDebuggerInfo
def dumpLeakLog
def processSingleLeakFile
def processLeakLog
def replaceBackSlashes
def wrapCommand

Variables

list __all__
dictionary DEBUGGER_INFO
tuple log = logging.getLogger()

Function Documentation

def build.automationutils.addCommonOptions (   parser,
  defaults = {} 
)

Definition at line 146 of file automationutils.py.

00146 
00147 def addCommonOptions(parser, defaults={}):
00148   parser.add_option("--xre-path",
00149                     action = "store", type = "string", dest = "xrePath",
00150                     # individual scripts will set a sane default
00151                     default = None,
00152                     help = "absolute path to directory containing XRE (probably xulrunner)")
00153   if 'SYMBOLS_PATH' not in defaults:
00154     defaults['SYMBOLS_PATH'] = None
00155   parser.add_option("--symbols-path",
00156                     action = "store", type = "string", dest = "symbolsPath",
00157                     default = defaults['SYMBOLS_PATH'],
00158                     help = "absolute path to directory containing breakpad symbols, or the URL of a zip file containing symbols")
00159   parser.add_option("--debugger",
00160                     action = "store", dest = "debugger",
00161                     help = "use the given debugger to launch the application")
00162   parser.add_option("--debugger-args",
00163                     action = "store", dest = "debuggerArgs",
00164                     help = "pass the given args to the debugger _before_ "
00165                            "the application on the command line")
00166   parser.add_option("--debugger-interactive",
00167                     action = "store_true", dest = "debuggerInteractive",
00168                     help = "prevents the test harness from redirecting "
00169                         "stdout and stderr for interactive debuggers")

def build.automationutils.checkForCrashes (   dumpDir,
  symbolsPath,
  testName = None 
)

Definition at line 170 of file automationutils.py.

00170 
00171 def checkForCrashes(dumpDir, symbolsPath, testName=None):
00172   stackwalkPath = os.environ.get('MINIDUMP_STACKWALK', None)
00173   # try to get the caller's filename if no test name is given
00174   if testName is None:
00175     try:
00176       testName = os.path.basename(sys._getframe(1).f_code.co_filename)
00177     except:
00178       testName = "unknown"
00179 
00180   # Check preconditions
00181   dumps = glob.glob(os.path.join(dumpDir, '*.dmp'))
00182   if len(dumps) == 0:
00183     return False
00184 
00185   foundCrash = False
00186   removeSymbolsPath = False
00187 
00188   # If our symbols are at a remote URL, download them now
00189   if isURL(symbolsPath):
00190     print "Downloading symbols from: " + symbolsPath
00191     removeSymbolsPath = True
00192     # Get the symbols and write them to a temporary zipfile
00193     data = urllib2.urlopen(symbolsPath)
00194     symbolsFile = tempfile.TemporaryFile()
00195     symbolsFile.write(data.read())
00196     # extract symbols to a temporary directory (which we'll delete after
00197     # processing all crashes)
00198     symbolsPath = tempfile.mkdtemp()
00199     zfile = ZipFileReader(symbolsFile)
00200     zfile.extractall(symbolsPath)
00201 
00202   try:
00203     for d in dumps:
00204       log.info("PROCESS-CRASH | %s | application crashed (minidump found)", testName)
00205       print "Crash dump filename: " + d
00206       if symbolsPath and stackwalkPath and os.path.exists(stackwalkPath):
00207         # run minidump stackwalk
00208         p = subprocess.Popen([stackwalkPath, d, symbolsPath],
00209                              stdout=subprocess.PIPE,
00210                              stderr=subprocess.PIPE)
00211         (out, err) = p.communicate()
00212         if len(out) > 3:
00213           # minidump_stackwalk is chatty, so ignore stderr when it succeeds.
00214           print out
00215         else:
00216           print "stderr from minidump_stackwalk:"
00217           print err
00218         if p.returncode != 0:
00219           print "minidump_stackwalk exited with return code %d" % p.returncode
00220       else:
00221         if not symbolsPath:
00222           print "No symbols path given, can't process dump."
00223         if not stackwalkPath:
00224           print "MINIDUMP_STACKWALK not set, can't process dump."
00225         elif stackwalkPath and not os.path.exists(stackwalkPath):
00226           print "MINIDUMP_STACKWALK binary not found: %s" % stackwalkPath
00227       dumpSavePath = os.environ.get('MINIDUMP_SAVE_PATH', None)
00228       if dumpSavePath:
00229         shutil.move(d, dumpSavePath)
00230         print "Saved dump as %s" % os.path.join(dumpSavePath,
00231                                                 os.path.basename(d))
00232       else:
00233         os.remove(d)
00234       extra = os.path.splitext(d)[0] + ".extra"
00235       if os.path.exists(extra):
00236         os.remove(extra)
00237       foundCrash = True
00238   finally:
00239     if removeSymbolsPath:
00240       shutil.rmtree(symbolsPath)
00241 
00242   return foundCrash

Here is the call graph for this function:

def build.automationutils.dumpLeakLog (   leakLogFile,
  filter = False 
)
Process the leak log, without parsing it.

Use this function if you want the raw log only.
Use it preferably with the |XPCOM_MEM_LEAK_LOG| environment variable.

Definition at line 294 of file automationutils.py.

00294 
00295 def dumpLeakLog(leakLogFile, filter = False):
00296   """Process the leak log, without parsing it.
00297 
00298   Use this function if you want the raw log only.
00299   Use it preferably with the |XPCOM_MEM_LEAK_LOG| environment variable.
00300   """
00301 
00302   # Don't warn (nor "info") if the log file is not there.
00303   if not os.path.exists(leakLogFile):
00304     return
00305 
00306   leaks = open(leakLogFile, "r")
00307   leakReport = leaks.read()
00308   leaks.close()
00309 
00310   # Only |XPCOM_MEM_LEAK_LOG| reports can be actually filtered out.
00311   # Only check whether an actual leak was reported.
00312   if filter and not "0 TOTAL " in leakReport:
00313     return
00314 
00315   # Simply copy the log.
00316   log.info(leakReport.rstrip("\n"))

def build.automationutils.getDebuggerInfo (   directory,
  debugger,
  debuggerArgs,
  debuggerInteractive = False 
)

Definition at line 263 of file automationutils.py.

00263 
00264 def getDebuggerInfo(directory, debugger, debuggerArgs, debuggerInteractive = False):
00265 
00266   debuggerInfo = None
00267 
00268   if debugger:
00269     debuggerPath = searchPath(directory, debugger)
00270     if not debuggerPath:
00271       print "Error: Path %s doesn't exist." % debugger
00272       sys.exit(1)
00273 
00274     debuggerName = os.path.basename(debuggerPath).lower()
00275 
00276     def getDebuggerInfo(type, default):
00277       if debuggerName in DEBUGGER_INFO and type in DEBUGGER_INFO[debuggerName]:
00278         return DEBUGGER_INFO[debuggerName][type]
00279       return default
00280 
00281     debuggerInfo = {
00282       "path": debuggerPath,
00283       "interactive" : getDebuggerInfo("interactive", False),
00284       "args": getDebuggerInfo("args", "").split()
00285     }
00286 
00287     if debuggerArgs:
00288       debuggerInfo["args"] = debuggerArgs.split()
00289     if debuggerInteractive:
00290       debuggerInfo["interactive"] = debuggerInteractive
00291 
00292   return debuggerInfo
00293 

Here is the call graph for this function:

def build.automationutils.getFullPath (   directory,
  path 
)

Definition at line 243 of file automationutils.py.

00243 
00244 def getFullPath(directory, path):
00245   "Get an absolute path relative to 'directory'."
00246   return os.path.normpath(os.path.join(directory, os.path.expanduser(path)))

Here is the caller graph for this function:

def build.automationutils.isURL (   thing)
Return True if |thing| looks like a URL.

Definition at line 141 of file automationutils.py.

00141 
00142 def isURL(thing):
00143   """Return True if |thing| looks like a URL."""
00144   # We want to download URLs like http://... but not Windows paths like c:\...
00145   return len(urlparse(thing).scheme) >= 2

Here is the caller graph for this function:

def build.automationutils.processLeakLog (   leakLogFile,
  leakThreshold = 0 
)
Process the leak log, including separate leak logs created
by child processes.

Use this function if you want an additional PASS/FAIL summary.
It must be used with the |XPCOM_MEM_BLOAT_LOG| environment variable.

Definition at line 411 of file automationutils.py.

00411 
00412 def processLeakLog(leakLogFile, leakThreshold = 0):
00413   """Process the leak log, including separate leak logs created
00414   by child processes.
00415 
00416   Use this function if you want an additional PASS/FAIL summary.
00417   It must be used with the |XPCOM_MEM_BLOAT_LOG| environment variable.
00418   """
00419 
00420   if not os.path.exists(leakLogFile):
00421     log.info("WARNING | automationutils.processLeakLog() | refcount logging is off, so leaks can't be detected!")
00422     return
00423 
00424   (leakLogFileDir, leakFileBase) = os.path.split(leakLogFile)
00425   pidRegExp = re.compile(r".*?_([a-z]*)_pid(\d*)$")
00426   if leakFileBase[-4:] == ".log":
00427     leakFileBase = leakFileBase[:-4]
00428     pidRegExp = re.compile(r".*?_([a-z]*)_pid(\d*).log$")
00429 
00430   for fileName in os.listdir(leakLogFileDir):
00431     if fileName.find(leakFileBase) != -1:
00432       thisFile = os.path.join(leakLogFileDir, fileName)
00433       processPID = 0
00434       processType = None
00435       m = pidRegExp.search(fileName)
00436       if m:
00437         processType = m.group(1)
00438         processPID = m.group(2)
00439       processSingleLeakFile(thisFile, processPID, processType, leakThreshold)

Here is the call graph for this function:

def build.automationutils.processSingleLeakFile (   leakLogFileName,
  PID,
  processType,
  leakThreshold 
)
Process a single leak log, corresponding to the specified
process PID and type.

Definition at line 317 of file automationutils.py.

00317 
00318 def processSingleLeakFile(leakLogFileName, PID, processType, leakThreshold):
00319   """Process a single leak log, corresponding to the specified
00320   process PID and type.
00321   """
00322 
00323   #                  Per-Inst  Leaked      Total  Rem ...
00324   #   0 TOTAL              17     192  419115886    2 ...
00325   # 833 nsTimerImpl        60     120      24726    2 ...
00326   lineRe = re.compile(r"^\s*\d+\s+(?P<name>\S+)\s+"
00327                       r"(?P<size>-?\d+)\s+(?P<bytesLeaked>-?\d+)\s+"
00328                       r"-?\d+\s+(?P<numLeaked>-?\d+)")
00329 
00330   processString = ""
00331   if PID and processType:
00332     processString = "| %s process %s " % (processType, PID)
00333   leaks = open(leakLogFileName, "r")
00334   for line in leaks:
00335     matches = lineRe.match(line)
00336     if (matches and
00337         int(matches.group("numLeaked")) == 0 and
00338         matches.group("name") != "TOTAL"):
00339       continue
00340     log.info(line.rstrip())
00341   leaks.close()
00342 
00343   leaks = open(leakLogFileName, "r")
00344   seenTotal = False
00345   crashedOnPurpose = False
00346   prefix = "TEST-PASS"
00347   numObjects = 0
00348   for line in leaks:
00349     if line.find("purposefully crash") > -1:
00350       crashedOnPurpose = True
00351     matches = lineRe.match(line)
00352     if not matches:
00353       continue
00354     name = matches.group("name")
00355     size = int(matches.group("size"))
00356     bytesLeaked = int(matches.group("bytesLeaked"))
00357     numLeaked = int(matches.group("numLeaked"))
00358     if size < 0 or bytesLeaked < 0 or numLeaked < 0:
00359       log.info("TEST-UNEXPECTED-FAIL %s| automationutils.processLeakLog() | negative leaks caught!" %
00360                processString)
00361       if name == "TOTAL":
00362         seenTotal = True
00363     elif name == "TOTAL":
00364       seenTotal = True
00365       # Check for leaks.
00366       if bytesLeaked < 0 or bytesLeaked > leakThreshold:
00367         prefix = "TEST-UNEXPECTED-FAIL"
00368         leakLog = "TEST-UNEXPECTED-FAIL %s| automationutils.processLeakLog() | leaked" \
00369                   " %d bytes during test execution" % (processString, bytesLeaked)
00370       elif bytesLeaked > 0:
00371         leakLog = "TEST-PASS %s| automationutils.processLeakLog() | WARNING leaked" \
00372                   " %d bytes during test execution" % (processString, bytesLeaked)
00373       else:
00374         leakLog = "TEST-PASS %s| automationutils.processLeakLog() | no leaks detected!" \
00375                   % processString
00376       # Remind the threshold if it is not 0, which is the default/goal.
00377       if leakThreshold != 0:
00378         leakLog += " (threshold set at %d bytes)" % leakThreshold
00379       # Log the information.
00380       log.info(leakLog)
00381     else:
00382       if numLeaked != 0:
00383         if numLeaked > 1:
00384           instance = "instances"
00385           rest = " each (%s bytes total)" % matches.group("bytesLeaked")
00386         else:
00387           instance = "instance"
00388           rest = ""
00389         numObjects += 1
00390         if numObjects > 5:
00391           # don't spam brief tinderbox logs with tons of leak output
00392           prefix = "TEST-INFO"
00393         log.info("%(prefix)s %(process)s| automationutils.processLeakLog() | leaked %(numLeaked)d %(instance)s of %(name)s "
00394                  "with size %(size)s bytes%(rest)s" %
00395                  { "prefix": prefix,
00396                    "process": processString,
00397                    "numLeaked": numLeaked,
00398                    "instance": instance,
00399                    "name": name,
00400                    "size": matches.group("size"),
00401                    "rest": rest })
00402   if not seenTotal:
00403     if crashedOnPurpose:
00404       log.info("INFO | automationutils.processLeakLog() | process %s was " \
00405                "deliberately crashed and thus has no leak log" % PID)
00406     else:
00407       log.info("TEST-UNEXPECTED-FAIL %s| automationutils.processLeakLog() | missing output line for total leaks!" %
00408              processString)
00409   leaks.close()
00410 

Here is the caller graph for this function:

Definition at line 440 of file automationutils.py.

00440 
00441 def replaceBackSlashes(input):
00442   return input.replace('\\', '/')

def build.automationutils.searchPath (   directory,
  path 
)

Definition at line 247 of file automationutils.py.

00247 
00248 def searchPath(directory, path):
00249   "Go one step beyond getFullPath and try the various folders in PATH"
00250   # Try looking in the current working directory first.
00251   newpath = getFullPath(directory, path)
00252   if os.path.isfile(newpath):
00253     return newpath
00254 
00255   # At this point we have to fail if a directory was given (to prevent cases
00256   # like './gdb' from matching '/usr/bin/./gdb').
00257   if not os.path.dirname(path):
00258     for dir in os.environ['PATH'].split(os.pathsep):
00259       newpath = os.path.join(dir, path)
00260       if os.path.isfile(newpath):
00261         return newpath
00262   return None

Here is the call graph for this function:

Here is the caller graph for this function:

If running on OS X 10.5 or older, wrap |cmd| so that it will
be executed as an i386 binary, in case it's a 32-bit/64-bit universal
binary.

Definition at line 443 of file automationutils.py.

00443 
00444 def wrapCommand(cmd):
00445   """
00446   If running on OS X 10.5 or older, wrap |cmd| so that it will
00447   be executed as an i386 binary, in case it's a 32-bit/64-bit universal
00448   binary.
00449   """
00450   if platform.system() == "Darwin" and \
00451      hasattr(platform, 'mac_ver') and \
00452      platform.mac_ver()[0][:4] < '10.6':
00453     return ["arch", "-arch", "i386"] + cmd
00454   # otherwise just execute the command normally
00455   return cmd


Variable Documentation

Initial value:
00001 [
00002   "ZipFileReader",
00003   "addCommonOptions",
00004   "checkForCrashes",
00005   "dumpLeakLog",
00006   "isURL",
00007   "processLeakLog",
00008   "getDebuggerInfo",
00009   "DEBUGGER_INFO",
00010   "replaceBackSlashes",
00011   "wrapCommand",
00012   "ShutdownLeakLogger"
00013   ]

Definition at line 45 of file automationutils.py.

Initial value:
00001 {
00002   # gdb requires that you supply the '--args' flag in order to pass arguments
00003   # after the executable name to the executable.
00004   "gdb": {
00005     "interactive": True,
00006     "args": "-q --args"
00007   },
00008 
00009   # valgrind doesn't explain much about leaks unless you set the
00010   # '--leak-check=full' flag.
00011   "valgrind": {
00012     "interactive": False,
00013     "args": "--leak-check=full"
00014   }
00015 }

Definition at line 61 of file automationutils.py.

tuple build.automationutils.log = logging.getLogger()

Definition at line 139 of file automationutils.py.