mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-04 02:57:38 +00:00
176 lines
7.1 KiB
Python
176 lines
7.1 KiB
Python
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
from __future__ import absolute_import, print_function, unicode_literals
|
|
|
|
import os
|
|
import subprocess
|
|
|
|
from mach.decorators import (
|
|
Command,
|
|
CommandArgument,
|
|
CommandProvider,
|
|
)
|
|
from mozbuild.base import (
|
|
MachCommandBase,
|
|
MachCommandConditions as conditions,
|
|
)
|
|
|
|
|
|
def is_valgrind_build(cls):
|
|
'''Must be a build with --enable-valgrind and --disable-jemalloc.'''
|
|
defines = cls.config_environment.defines
|
|
return 'MOZ_VALGRIND' in defines and 'MOZ_MEMORY' not in defines
|
|
|
|
|
|
@CommandProvider
|
|
class MachCommands(MachCommandBase):
|
|
'''
|
|
Run Valgrind tests.
|
|
'''
|
|
def __init__(self, context):
|
|
MachCommandBase.__init__(self, context)
|
|
|
|
@Command('valgrind-test', category='testing',
|
|
conditions=[conditions.is_firefox, is_valgrind_build],
|
|
description='Run the Valgrind test job (memory-related errors).')
|
|
@CommandArgument('--suppressions', default=[], action='append',
|
|
metavar='FILENAME',
|
|
help='Specify a suppression file for Valgrind to use. Use '
|
|
'--suppression multiple times to specify multiple suppression '
|
|
'files.')
|
|
def valgrind_test(self, suppressions):
|
|
import json
|
|
import sys
|
|
import tempfile
|
|
|
|
from mozbuild.base import MozbuildObject
|
|
from mozfile import TemporaryDirectory
|
|
from mozhttpd import MozHttpd
|
|
from mozprofile import FirefoxProfile, Preferences
|
|
from mozprofile.permissions import ServerLocations
|
|
from mozrunner import FirefoxRunner
|
|
from mozrunner.utils import findInPath
|
|
from valgrind.output_handler import OutputHandler
|
|
|
|
build_dir = os.path.join(self.topsrcdir, 'build')
|
|
|
|
# XXX: currently we just use the PGO inputs for Valgrind runs. This may
|
|
# change in the future.
|
|
httpd = MozHttpd(docroot=os.path.join(build_dir, 'pgo'))
|
|
httpd.start(block=False)
|
|
|
|
with TemporaryDirectory() as profilePath:
|
|
#TODO: refactor this into mozprofile
|
|
prefpath = os.path.join(self.topsrcdir, 'testing', 'profiles', 'prefs_general.js')
|
|
prefs = {}
|
|
prefs.update(Preferences.read_prefs(prefpath))
|
|
interpolation = { 'server': '%s:%d' % httpd.httpd.server_address,
|
|
'OOP': 'false'}
|
|
prefs = json.loads(json.dumps(prefs) % interpolation)
|
|
for pref in prefs:
|
|
prefs[pref] = Preferences.cast(prefs[pref])
|
|
|
|
quitter = os.path.join(self.distdir, 'xpi-stage', 'quitter')
|
|
|
|
locations = ServerLocations()
|
|
locations.add_host(host='127.0.0.1',
|
|
port=httpd.httpd.server_port,
|
|
options='primary')
|
|
|
|
profile = FirefoxProfile(profile=profilePath,
|
|
preferences=prefs,
|
|
addons=[quitter],
|
|
locations=locations)
|
|
|
|
firefox_args = [httpd.get_url()]
|
|
|
|
env = os.environ.copy()
|
|
env['G_SLICE'] = 'always-malloc'
|
|
env['MOZ_CC_RUN_DURING_SHUTDOWN'] = '1'
|
|
env['MOZ_CRASHREPORTER_NO_REPORT'] = '1'
|
|
env['XPCOM_DEBUG_BREAK'] = 'warn'
|
|
|
|
env.update(self.extra_environment_variables)
|
|
|
|
outputHandler = OutputHandler()
|
|
kp_kwargs = {'processOutputLine': [outputHandler]}
|
|
|
|
valgrind = 'valgrind'
|
|
if not os.path.exists(valgrind):
|
|
valgrind = findInPath(valgrind)
|
|
|
|
valgrind_args = [
|
|
valgrind,
|
|
'--smc-check=all-non-file',
|
|
'--vex-iropt-register-updates=allregs-at-mem-access',
|
|
'--gen-suppressions=all',
|
|
'--num-callers=36',
|
|
'--leak-check=full',
|
|
'--show-possibly-lost=no',
|
|
'--track-origins=yes',
|
|
'--trace-children=yes',
|
|
# The gstreamer plugin scanner can run as part of executing
|
|
# firefox, but is an external program. In some weird cases,
|
|
# valgrind finds errors while executing __libc_freeres when
|
|
# it runs, but those are not relevant, as it's related to
|
|
# executing third party code. So don't trace
|
|
# gst-plugin-scanner.
|
|
'--trace-children-skip=*/gst-plugin-scanner',
|
|
'-v', # Enable verbosity to get the list of used suppressions
|
|
]
|
|
|
|
for s in suppressions:
|
|
valgrind_args.append('--suppressions=' + s)
|
|
|
|
supps_dir = os.path.join(build_dir, 'valgrind')
|
|
supps_file1 = os.path.join(supps_dir, 'cross-architecture.sup')
|
|
valgrind_args.append('--suppressions=' + supps_file1)
|
|
|
|
# MACHTYPE is an odd bash-only environment variable that doesn't
|
|
# show up in os.environ, so we have to get it another way.
|
|
machtype = subprocess.check_output(['bash', '-c', 'echo $MACHTYPE']).rstrip()
|
|
supps_file2 = os.path.join(supps_dir, machtype + '.sup')
|
|
if os.path.isfile(supps_file2):
|
|
valgrind_args.append('--suppressions=' + supps_file2)
|
|
|
|
exitcode = None
|
|
timeout = 1100
|
|
try:
|
|
runner = FirefoxRunner(profile=profile,
|
|
binary=self.get_binary_path(),
|
|
cmdargs=firefox_args,
|
|
env=env,
|
|
process_args=kp_kwargs)
|
|
runner.start(debug_args=valgrind_args)
|
|
# This timeout is slightly less than the no-output timeout on
|
|
# TBPL, so we'll timeout here first and give an informative
|
|
# message.
|
|
exitcode = runner.wait(timeout=timeout)
|
|
|
|
finally:
|
|
errs = outputHandler.error_count
|
|
supps = outputHandler.suppression_count
|
|
if errs != supps:
|
|
status = 1 # turns the TBPL job orange
|
|
print('TEST-UNEXPECTED-FAIL | valgrind-test | error parsing: {} errors seen, but {} generated suppressions seen'.format(errs, supps))
|
|
|
|
elif errs == 0:
|
|
status = 0
|
|
print('TEST-PASS | valgrind-test | valgrind found no errors')
|
|
else:
|
|
status = 1 # turns the TBPL job orange
|
|
# We've already printed details of the errors.
|
|
|
|
if exitcode == None:
|
|
status = 2 # turns the TBPL job red
|
|
print('TEST-UNEXPECTED-FAIL | valgrind-test | Valgrind timed out (reached {} second limit)'.format(timeout))
|
|
elif exitcode != 0:
|
|
status = 2 # turns the TBPL job red
|
|
print('TEST-UNEXPECTED-FAIL | valgrind-test | non-zero exit code from Valgrind')
|
|
|
|
httpd.stop()
|
|
|
|
return status
|