Bug 1560374 - Set testharness and reftest web-platform-tests to Tier-1; r=jmaher...
[gecko.git] / build / valgrind / mach_commands.py
blobd12b0a3575fe58f62150853eb889b3185ac35bdc
1 # This Source Code Form is subject to the terms of the Mozilla Public
2 # License, v. 2.0. If a copy of the MPL was not distributed with this
3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 from __future__ import absolute_import, unicode_literals
7 import json
8 import logging
9 import mozinfo
10 import os
12 from mach.decorators import (
13 Command,
14 CommandArgument,
15 CommandProvider,
17 from mozbuild.base import (
18 MachCommandBase,
19 MachCommandConditions as conditions,
23 def is_valgrind_build(cls):
24 '''Must be a build with --enable-valgrind and --disable-jemalloc.'''
25 defines = cls.config_environment.defines
26 return 'MOZ_VALGRIND' in defines and 'MOZ_MEMORY' not in defines
29 @CommandProvider
30 class MachCommands(MachCommandBase):
31 '''
32 Run Valgrind tests.
33 '''
35 def __init__(self, context):
36 MachCommandBase.__init__(self, context)
38 @Command('valgrind-test', category='testing',
39 conditions=[conditions.is_firefox, is_valgrind_build],
40 description='Run the Valgrind test job (memory-related errors).')
41 @CommandArgument('--suppressions', default=[], action='append',
42 metavar='FILENAME',
43 help='Specify a suppression file for Valgrind to use. Use '
44 '--suppression multiple times to specify multiple suppression '
45 'files.')
46 def valgrind_test(self, suppressions):
48 from mozfile import TemporaryDirectory
49 from mozhttpd import MozHttpd
50 from mozprofile import FirefoxProfile, Preferences
51 from mozprofile.permissions import ServerLocations
52 from mozrunner import FirefoxRunner
53 from mozrunner.utils import findInPath
54 from six import string_types
55 from valgrind.output_handler import OutputHandler
57 build_dir = os.path.join(self.topsrcdir, 'build')
59 # XXX: currently we just use the PGO inputs for Valgrind runs. This may
60 # change in the future.
61 httpd = MozHttpd(docroot=os.path.join(build_dir, 'pgo'))
62 httpd.start(block=False)
64 with TemporaryDirectory() as profilePath:
65 # TODO: refactor this into mozprofile
66 profile_data_dir = os.path.join(
67 self.topsrcdir, 'testing', 'profiles')
68 with open(os.path.join(profile_data_dir, 'profiles.json'), 'r') as fh:
69 base_profiles = json.load(fh)['valgrind']
71 prefpaths = [os.path.join(profile_data_dir, profile, 'user.js')
72 for profile in base_profiles]
73 prefs = {}
74 for path in prefpaths:
75 prefs.update(Preferences.read_prefs(path))
77 interpolation = {
78 'server': '%s:%d' % httpd.httpd.server_address,
80 for k, v in prefs.items():
81 if isinstance(v, string_types):
82 v = v.format(**interpolation)
83 prefs[k] = Preferences.cast(v)
85 quitter = os.path.join(
86 self.topsrcdir, 'tools', 'quitter', 'quitter@mozilla.org.xpi')
88 locations = ServerLocations()
89 locations.add_host(host='127.0.0.1',
90 port=httpd.httpd.server_port,
91 options='primary')
93 profile = FirefoxProfile(profile=profilePath,
94 preferences=prefs,
95 addons=[quitter],
96 locations=locations)
98 firefox_args = [httpd.get_url()]
100 env = os.environ.copy()
101 env['G_SLICE'] = 'always-malloc'
102 env['MOZ_CC_RUN_DURING_SHUTDOWN'] = '1'
103 env['MOZ_CRASHREPORTER_NO_REPORT'] = '1'
104 env['MOZ_DISABLE_NONLOCAL_CONNECTIONS'] = '1'
105 env['XPCOM_DEBUG_BREAK'] = 'warn'
107 env.update(self.extra_environment_variables)
109 outputHandler = OutputHandler(self.log)
110 kp_kwargs = {'processOutputLine': [outputHandler]}
112 valgrind = 'valgrind'
113 if not os.path.exists(valgrind):
114 valgrind = findInPath(valgrind)
116 valgrind_args = [
117 valgrind,
118 '--sym-offsets=yes',
119 '--smc-check=all-non-file',
120 '--vex-iropt-register-updates=allregs-at-mem-access',
121 '--gen-suppressions=all',
122 '--num-callers=36',
123 '--leak-check=full',
124 '--show-possibly-lost=no',
125 '--track-origins=yes',
126 '--trace-children=yes',
127 '-v', # Enable verbosity to get the list of used suppressions
128 # Avoid excessive delays in the presence of spinlocks.
129 # See bug 1309851.
130 '--fair-sched=yes',
131 # Keep debuginfo after library unmap. See bug 1382280.
132 '--keep-debuginfo=yes',
133 # Reduce noise level on rustc and/or LLVM compiled code.
134 # See bug 1365915
135 '--expensive-definedness-checks=yes',
138 for s in suppressions:
139 valgrind_args.append('--suppressions=' + s)
141 supps_dir = os.path.join(build_dir, 'valgrind')
142 supps_file1 = os.path.join(supps_dir, 'cross-architecture.sup')
143 valgrind_args.append('--suppressions=' + supps_file1)
145 if mozinfo.os == 'linux':
146 machtype = {
147 'x86_64': 'x86_64-pc-linux-gnu',
148 'x86': 'i386-pc-linux-gnu',
149 }.get(mozinfo.processor)
150 if machtype:
151 supps_file2 = os.path.join(supps_dir, machtype + '.sup')
152 if os.path.isfile(supps_file2):
153 valgrind_args.append('--suppressions=' + supps_file2)
155 exitcode = None
156 timeout = 1800
157 try:
158 runner = FirefoxRunner(profile=profile,
159 binary=self.get_binary_path(),
160 cmdargs=firefox_args,
161 env=env,
162 process_args=kp_kwargs)
163 runner.start(debug_args=valgrind_args)
164 exitcode = runner.wait(timeout=timeout)
166 finally:
167 errs = outputHandler.error_count
168 supps = outputHandler.suppression_count
169 if errs != supps:
170 status = 1 # turns the TBPL job orange
171 self.log(logging.ERROR, 'valgrind-fail-parsing',
172 {'errs': errs, 'supps': supps},
173 'TEST-UNEXPECTED-FAIL | valgrind-test | error parsing: {errs} errors '
174 'seen, but {supps} generated suppressions seen')
176 elif errs == 0:
177 status = 0
178 self.log(logging.INFO, 'valgrind-pass', {},
179 'TEST-PASS | valgrind-test | valgrind found no errors')
180 else:
181 status = 1 # turns the TBPL job orange
182 # We've already printed details of the errors.
184 if exitcode is None:
185 status = 2 # turns the TBPL job red
186 self.log(logging.ERROR, 'valgrind-fail-timeout',
187 {'timeout': timeout},
188 'TEST-UNEXPECTED-FAIL | valgrind-test | Valgrind timed out '
189 '(reached {timeout} second limit)')
190 elif exitcode != 0:
191 status = 2 # turns the TBPL job red
192 self.log(logging.ERROR, 'valgrind-fail-errors',
193 {'exitcode': exitcode},
194 'TEST-UNEXPECTED-FAIL | valgrind-test | non-zero exit code '
195 'from Valgrind: {exitcode}')
197 httpd.stop()
199 return status