1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/build/valgrind/mach_commands.py Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,158 @@ 1.4 +# This Source Code Form is subject to the terms of the Mozilla Public 1.5 +# License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 +# file, You can obtain one at http://mozilla.org/MPL/2.0/. 1.7 + 1.8 +from __future__ import print_function, unicode_literals 1.9 + 1.10 +import os 1.11 +import re 1.12 +import subprocess 1.13 + 1.14 +from mach.decorators import ( 1.15 + Command, 1.16 + CommandArgument, 1.17 + CommandProvider, 1.18 +) 1.19 +from mozbuild.base import ( 1.20 + MachCommandBase, 1.21 + MachCommandConditions as conditions, 1.22 +) 1.23 + 1.24 + 1.25 +def is_valgrind_build(cls): 1.26 + '''Must be a build with --enable-valgrind and --disable-jemalloc.''' 1.27 + defines = cls.config_environment.defines 1.28 + return 'MOZ_VALGRIND' in defines and 'MOZ_MEMORY' not in defines 1.29 + 1.30 + 1.31 +@CommandProvider 1.32 +class MachCommands(MachCommandBase): 1.33 + ''' 1.34 + Run Valgrind tests. 1.35 + ''' 1.36 + def __init__(self, context): 1.37 + MachCommandBase.__init__(self, context) 1.38 + 1.39 + @Command('valgrind-test', category='testing', 1.40 + conditions=[conditions.is_firefox, is_valgrind_build], 1.41 + description='Run the Valgrind test job.') 1.42 + @CommandArgument('--suppressions', default=[], action='append', 1.43 + metavar='FILENAME', 1.44 + help='Specify a suppression file for Valgrind to use. Use ' 1.45 + '--suppression multiple times to specify multiple suppression ' 1.46 + 'files.') 1.47 + def valgrind_test(self, suppressions): 1.48 + import json 1.49 + import sys 1.50 + import tempfile 1.51 + 1.52 + from mozbuild.base import MozbuildObject 1.53 + from mozfile import TemporaryDirectory 1.54 + from mozhttpd import MozHttpd 1.55 + from mozprofile import FirefoxProfile, Preferences 1.56 + from mozprofile.permissions import ServerLocations 1.57 + from mozrunner import FirefoxRunner 1.58 + from mozrunner.utils import findInPath 1.59 + from valgrind.output_handler import OutputHandler 1.60 + 1.61 + build_dir = os.path.join(self.topsrcdir, 'build') 1.62 + 1.63 + # XXX: currently we just use the PGO inputs for Valgrind runs. This may 1.64 + # change in the future. 1.65 + httpd = MozHttpd(docroot=os.path.join(build_dir, 'pgo')) 1.66 + httpd.start(block=False) 1.67 + 1.68 + with TemporaryDirectory() as profilePath: 1.69 + #TODO: refactor this into mozprofile 1.70 + prefpath = os.path.join(self.topsrcdir, 'testing', 'profiles', 'prefs_general.js') 1.71 + prefs = {} 1.72 + prefs.update(Preferences.read_prefs(prefpath)) 1.73 + interpolation = { 'server': '%s:%d' % httpd.httpd.server_address, 1.74 + 'OOP': 'false'} 1.75 + prefs = json.loads(json.dumps(prefs) % interpolation) 1.76 + for pref in prefs: 1.77 + prefs[pref] = Preferences.cast(prefs[pref]) 1.78 + 1.79 + quitter = os.path.join(self.distdir, 'xpi-stage', 'quitter') 1.80 + 1.81 + locations = ServerLocations() 1.82 + locations.add_host(host='127.0.0.1', 1.83 + port=httpd.httpd.server_port, 1.84 + options='primary') 1.85 + 1.86 + profile = FirefoxProfile(profile=profilePath, 1.87 + preferences=prefs, 1.88 + addons=[quitter], 1.89 + locations=locations) 1.90 + 1.91 + firefox_args = [httpd.get_url()] 1.92 + 1.93 + env = os.environ.copy() 1.94 + env['G_SLICE'] = 'always-malloc' 1.95 + env['MOZ_CC_RUN_DURING_SHUTDOWN'] = '1' 1.96 + env['MOZ_CRASHREPORTER_NO_REPORT'] = '1' 1.97 + env['XPCOM_DEBUG_BREAK'] = 'warn' 1.98 + 1.99 + outputHandler = OutputHandler() 1.100 + kp_kwargs = {'processOutputLine': [outputHandler]} 1.101 + 1.102 + valgrind = 'valgrind' 1.103 + if not os.path.exists(valgrind): 1.104 + valgrind = findInPath(valgrind) 1.105 + 1.106 + valgrind_args = [ 1.107 + valgrind, 1.108 + '--smc-check=all-non-file', 1.109 + '--vex-iropt-register-updates=allregs-at-mem-access', 1.110 + '--gen-suppressions=all', 1.111 + '--num-callers=36', 1.112 + '--leak-check=full', 1.113 + '--show-possibly-lost=no', 1.114 + '--track-origins=yes' 1.115 + ] 1.116 + 1.117 + for s in suppressions: 1.118 + valgrind_args.append('--suppressions=' + s) 1.119 + 1.120 + supps_dir = os.path.join(build_dir, 'valgrind') 1.121 + supps_file1 = os.path.join(supps_dir, 'cross-architecture.sup') 1.122 + valgrind_args.append('--suppressions=' + supps_file1) 1.123 + 1.124 + # MACHTYPE is an odd bash-only environment variable that doesn't 1.125 + # show up in os.environ, so we have to get it another way. 1.126 + machtype = subprocess.check_output(['bash', '-c', 'echo $MACHTYPE']).rstrip() 1.127 + supps_file2 = os.path.join(supps_dir, machtype + '.sup') 1.128 + if os.path.isfile(supps_file2): 1.129 + valgrind_args.append('--suppressions=' + supps_file2) 1.130 + 1.131 + exitcode = None 1.132 + try: 1.133 + runner = FirefoxRunner(profile=profile, 1.134 + binary=self.get_binary_path(), 1.135 + cmdargs=firefox_args, 1.136 + env=env, 1.137 + kp_kwargs=kp_kwargs) 1.138 + runner.start(debug_args=valgrind_args) 1.139 + exitcode = runner.wait() 1.140 + 1.141 + finally: 1.142 + errs = outputHandler.error_count 1.143 + supps = outputHandler.suppression_count 1.144 + if errs != supps: 1.145 + status = 1 # turns the TBPL job orange 1.146 + print('TEST-UNEXPECTED-FAILURE | valgrind-test | error parsing:', errs, "errors seen, but", supps, "generated suppressions seen") 1.147 + 1.148 + elif errs == 0: 1.149 + status = 0 1.150 + print('TEST-PASS | valgrind-test | valgrind found no errors') 1.151 + else: 1.152 + status = 1 # turns the TBPL job orange 1.153 + # We've already printed details of the errors. 1.154 + 1.155 + if exitcode != 0: 1.156 + status = 2 # turns the TBPL job red 1.157 + print('TEST-UNEXPECTED-FAIL | valgrind-test | non-zero exit code from Valgrind') 1.158 + 1.159 + httpd.stop() 1.160 + 1.161 + return status