author Ted Mielczarek <>
Thu, 08 Oct 2009 14:10:47 -0400
changeset 33678 b65bcf3fa240c8f1538332471a671132bf0616ea
parent 33395 0f0239688a76c6a992598b57a4e67bfc45793e19
child 34020 0d4182d52170af7e3e85a38484cb293bddd9d147
permissions -rw-r--r--
bug 515436 - Electrolysis test runs need a special LD_LIBRARY_PATH. fix and various consumers to not overwrite LD_LIBRARY_PATH. r=bsmedberg

# Version: MPL 1.1/GPL 2.0/LGPL 2.1
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
# The Original Code is code.
# The Initial Developer of the Original Code is The Mozilla Foundation
# Portions created by the Initial Developer are Copyright (C) 2009
# the Initial Developer. All Rights Reserved.
# Contributor(s):
#   Serge Gautherie <>
#   Ted Mielczarek <>
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
# ***** END LICENSE BLOCK ***** */

import glob, logging, os, subprocess, sys
import re

__all__ = [

log = logging.getLogger()

def addCommonOptions(parser, defaults={}):
                    action = "store", type = "string", dest = "xrePath",
                    # individual scripts will set a sane default
                    default = None,
                    help = "absolute path to directory containing XRE (probably xulrunner)")
  if 'SYMBOLS_PATH' not in defaults:
    defaults['SYMBOLS_PATH'] = None
                    action = "store", type = "string", dest = "symbolsPath",
                    default = defaults['SYMBOLS_PATH'],
                    help = "absolute path to directory containing breakpad symbols")

def checkForCrashes(dumpDir, symbolsPath, testName=None):
  stackwalkPath = os.environ.get('MINIDUMP_STACKWALK', None)
  # try to get the caller's filename if no test name is given
  if testName is None:
      testName = os.path.basename(sys._getframe(1).f_code.co_filename)
      testName = "unknown"

  foundCrash = False
  dumps = glob.glob(os.path.join(dumpDir, '*.dmp'))
  for d in dumps:"TEST-UNEXPECTED-FAIL | %s | application crashed (minidump found)", testName)
    if symbolsPath and stackwalkPath:
      nullfd = open(os.devnull, 'w')
      # eat minidump_stackwalk errors[stackwalkPath, d, symbolsPath], stderr=nullfd)
      if not symbolsPath:
        print "No symbols path given, can't process dump."
      if not stackwalkPath:
        print "MINIDUMP_STACKWALK not set, can't process dump."
    extra = os.path.splitext(d)[0] + ".extra"
    if os.path.exists(extra):
    foundCrash = True

  return foundCrash

def 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.

  # Don't warn (nor "info") if the log file is not there.
  if not os.path.exists(leakLogFile):

  leaks = open(leakLogFile, "r")
  leakReport =

  # Only |XPCOM_MEM_LEAK_LOG| reports can be actually filtered out.
  # Only check whether an actual leak was reported.
  if filter and not "0 TOTAL " in leakReport:

  # Simply copy the log."\n"))

def processLeakLog(leakLogFile, leakThreshold = 0):
  """Process the leak log, parsing it.

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

  if not os.path.exists(leakLogFile):"WARNING | automationutils.processLeakLog() | refcount logging is off, so leaks can't be detected!")

  #                  Per-Inst  Leaked      Total  Rem ...
  #   0 TOTAL              17     192  419115886    2 ...
  # 833 nsTimerImpl        60     120      24726    2 ...
  lineRe = re.compile(r"^\s*\d+\s+(?P<name>\S+)\s+"

  leaks = open(leakLogFile, "r")
  for line in leaks:
    matches = lineRe.match(line)
    if (matches and
        int("numLeaked")) == 0 and"name") != "TOTAL"):

  leaks = open(leakLogFile, "r")
  seenTotal = False
  prefix = "TEST-PASS"
  for line in leaks:
    matches = lineRe.match(line)
    if not matches:
    name ="name")
    size = int("size"))
    bytesLeaked = int("bytesLeaked"))
    numLeaked = int("numLeaked"))
    if size < 0 or bytesLeaked < 0 or numLeaked < 0:"TEST-UNEXPECTED-FAIL | automationutils.processLeakLog() | negative leaks caught!")
      if name == "TOTAL":
        seenTotal = True
    elif name == "TOTAL":
      seenTotal = True
      # Check for leaks.
      if bytesLeaked < 0 or bytesLeaked > leakThreshold:
        prefix = "TEST-UNEXPECTED-FAIL"
        leakLog = "TEST-UNEXPECTED-FAIL | automationutils.processLeakLog() | leaked" \
                  " %d bytes during test execution" % bytesLeaked
      elif bytesLeaked > 0:
        leakLog = "TEST-PASS | automationutils.processLeakLog() | WARNING leaked" \
                  " %d bytes during test execution" % bytesLeaked
        leakLog = "TEST-PASS | automationutils.processLeakLog() | no leaks detected!"
      # Remind the threshold if it is not 0, which is the default/goal.
      if leakThreshold != 0:
        leakLog += " (threshold set at %d bytes)" % leakThreshold
      # Log the information.
      if numLeaked != 0:
        if numLeaked > 1:
          instance = "instances"
          rest = " each (%s bytes total)" %"bytesLeaked")
          instance = "instance"
          rest = """%(prefix)s | automationutils.processLeakLog() | leaked %(numLeaked)d %(instance)s of %(name)s "
                 "with size %(size)s bytes%(rest)s" %
                 { "prefix": prefix,
                   "numLeaked": numLeaked,
                   "instance": instance,
                   "name": name,
                   "rest": rest })
  if not seenTotal:"TEST-UNEXPECTED-FAIL | automationutils.processLeakLog() | missing output line for total leaks!")