Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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 print_function, unicode_literals
7 import os
8 import re
9 import subprocess
11 from mach.decorators import (
12 Command,
13 CommandArgument,
14 CommandProvider,
15 )
16 from mozbuild.base import (
17 MachCommandBase,
18 MachCommandConditions as conditions,
19 )
22 def is_valgrind_build(cls):
23 '''Must be a build with --enable-valgrind and --disable-jemalloc.'''
24 defines = cls.config_environment.defines
25 return 'MOZ_VALGRIND' in defines and 'MOZ_MEMORY' not in defines
28 @CommandProvider
29 class MachCommands(MachCommandBase):
30 '''
31 Run Valgrind tests.
32 '''
33 def __init__(self, context):
34 MachCommandBase.__init__(self, context)
36 @Command('valgrind-test', category='testing',
37 conditions=[conditions.is_firefox, is_valgrind_build],
38 description='Run the Valgrind test job.')
39 @CommandArgument('--suppressions', default=[], action='append',
40 metavar='FILENAME',
41 help='Specify a suppression file for Valgrind to use. Use '
42 '--suppression multiple times to specify multiple suppression '
43 'files.')
44 def valgrind_test(self, suppressions):
45 import json
46 import sys
47 import tempfile
49 from mozbuild.base import MozbuildObject
50 from mozfile import TemporaryDirectory
51 from mozhttpd import MozHttpd
52 from mozprofile import FirefoxProfile, Preferences
53 from mozprofile.permissions import ServerLocations
54 from mozrunner import FirefoxRunner
55 from mozrunner.utils import findInPath
56 from valgrind.output_handler import OutputHandler
58 build_dir = os.path.join(self.topsrcdir, 'build')
60 # XXX: currently we just use the PGO inputs for Valgrind runs. This may
61 # change in the future.
62 httpd = MozHttpd(docroot=os.path.join(build_dir, 'pgo'))
63 httpd.start(block=False)
65 with TemporaryDirectory() as profilePath:
66 #TODO: refactor this into mozprofile
67 prefpath = os.path.join(self.topsrcdir, 'testing', 'profiles', 'prefs_general.js')
68 prefs = {}
69 prefs.update(Preferences.read_prefs(prefpath))
70 interpolation = { 'server': '%s:%d' % httpd.httpd.server_address,
71 'OOP': 'false'}
72 prefs = json.loads(json.dumps(prefs) % interpolation)
73 for pref in prefs:
74 prefs[pref] = Preferences.cast(prefs[pref])
76 quitter = os.path.join(self.distdir, 'xpi-stage', 'quitter')
78 locations = ServerLocations()
79 locations.add_host(host='127.0.0.1',
80 port=httpd.httpd.server_port,
81 options='primary')
83 profile = FirefoxProfile(profile=profilePath,
84 preferences=prefs,
85 addons=[quitter],
86 locations=locations)
88 firefox_args = [httpd.get_url()]
90 env = os.environ.copy()
91 env['G_SLICE'] = 'always-malloc'
92 env['MOZ_CC_RUN_DURING_SHUTDOWN'] = '1'
93 env['MOZ_CRASHREPORTER_NO_REPORT'] = '1'
94 env['XPCOM_DEBUG_BREAK'] = 'warn'
96 outputHandler = OutputHandler()
97 kp_kwargs = {'processOutputLine': [outputHandler]}
99 valgrind = 'valgrind'
100 if not os.path.exists(valgrind):
101 valgrind = findInPath(valgrind)
103 valgrind_args = [
104 valgrind,
105 '--smc-check=all-non-file',
106 '--vex-iropt-register-updates=allregs-at-mem-access',
107 '--gen-suppressions=all',
108 '--num-callers=36',
109 '--leak-check=full',
110 '--show-possibly-lost=no',
111 '--track-origins=yes'
112 ]
114 for s in suppressions:
115 valgrind_args.append('--suppressions=' + s)
117 supps_dir = os.path.join(build_dir, 'valgrind')
118 supps_file1 = os.path.join(supps_dir, 'cross-architecture.sup')
119 valgrind_args.append('--suppressions=' + supps_file1)
121 # MACHTYPE is an odd bash-only environment variable that doesn't
122 # show up in os.environ, so we have to get it another way.
123 machtype = subprocess.check_output(['bash', '-c', 'echo $MACHTYPE']).rstrip()
124 supps_file2 = os.path.join(supps_dir, machtype + '.sup')
125 if os.path.isfile(supps_file2):
126 valgrind_args.append('--suppressions=' + supps_file2)
128 exitcode = None
129 try:
130 runner = FirefoxRunner(profile=profile,
131 binary=self.get_binary_path(),
132 cmdargs=firefox_args,
133 env=env,
134 kp_kwargs=kp_kwargs)
135 runner.start(debug_args=valgrind_args)
136 exitcode = runner.wait()
138 finally:
139 errs = outputHandler.error_count
140 supps = outputHandler.suppression_count
141 if errs != supps:
142 status = 1 # turns the TBPL job orange
143 print('TEST-UNEXPECTED-FAILURE | valgrind-test | error parsing:', errs, "errors seen, but", supps, "generated suppressions seen")
145 elif errs == 0:
146 status = 0
147 print('TEST-PASS | valgrind-test | valgrind found no errors')
148 else:
149 status = 1 # turns the TBPL job orange
150 # We've already printed details of the errors.
152 if exitcode != 0:
153 status = 2 # turns the TBPL job red
154 print('TEST-UNEXPECTED-FAIL | valgrind-test | non-zero exit code from Valgrind')
156 httpd.stop()
158 return status