Bug 1690340 - Part 2: Use the new naming for the developer tools menu items. r=jdescottes
[gecko.git] / build / valgrind / mach_commands.py
blob408c0be28c14fabc7f6948353d563fe63971cfa4
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,
20 BinaryNotFoundException,
24 def is_valgrind_build(cls):
25 """Must be a build with --enable-valgrind and --disable-jemalloc."""
26 defines = cls.config_environment.defines
27 return "MOZ_VALGRIND" in defines and "MOZ_MEMORY" not in defines
30 @CommandProvider
31 class MachCommands(MachCommandBase):
32 """
33 Run Valgrind tests.
34 """
36 @Command(
37 "valgrind-test",
38 category="testing",
39 conditions=[conditions.is_firefox_or_thunderbird, is_valgrind_build],
40 description="Run the Valgrind test job (memory-related errors).",
42 @CommandArgument(
43 "--suppressions",
44 default=[],
45 action="append",
46 metavar="FILENAME",
47 help="Specify a suppression file for Valgrind to use. Use "
48 "--suppression multiple times to specify multiple suppression "
49 "files.",
51 def valgrind_test(self, suppressions):
53 from mozfile import TemporaryDirectory
54 from mozhttpd import MozHttpd
55 from mozprofile import FirefoxProfile, Preferences
56 from mozprofile.permissions import ServerLocations
57 from mozrunner import FirefoxRunner
58 from mozrunner.utils import findInPath
59 from six import string_types
60 from valgrind.output_handler import OutputHandler
62 build_dir = os.path.join(self.topsrcdir, "build")
64 # XXX: currently we just use the PGO inputs for Valgrind runs. This may
65 # change in the future.
66 httpd = MozHttpd(docroot=os.path.join(build_dir, "pgo"))
67 httpd.start(block=False)
69 with TemporaryDirectory() as profilePath:
70 # TODO: refactor this into mozprofile
71 profile_data_dir = os.path.join(self.topsrcdir, "testing", "profiles")
72 with open(os.path.join(profile_data_dir, "profiles.json"), "r") as fh:
73 base_profiles = json.load(fh)["valgrind"]
75 prefpaths = [
76 os.path.join(profile_data_dir, profile, "user.js")
77 for profile in base_profiles
79 prefs = {}
80 for path in prefpaths:
81 prefs.update(Preferences.read_prefs(path))
83 interpolation = {
84 "server": "%s:%d" % httpd.httpd.server_address,
86 for k, v in prefs.items():
87 if isinstance(v, string_types):
88 v = v.format(**interpolation)
89 prefs[k] = Preferences.cast(v)
91 quitter = os.path.join(
92 self.topsrcdir, "tools", "quitter", "quitter@mozilla.org.xpi"
95 locations = ServerLocations()
96 locations.add_host(
97 host="127.0.0.1", port=httpd.httpd.server_port, options="primary"
100 profile = FirefoxProfile(
101 profile=profilePath,
102 preferences=prefs,
103 addons=[quitter],
104 locations=locations,
107 firefox_args = [httpd.get_url()]
109 env = os.environ.copy()
110 env["G_SLICE"] = "always-malloc"
111 env["MOZ_CC_RUN_DURING_SHUTDOWN"] = "1"
112 env["MOZ_CRASHREPORTER_NO_REPORT"] = "1"
113 env["MOZ_DISABLE_NONLOCAL_CONNECTIONS"] = "1"
114 env["XPCOM_DEBUG_BREAK"] = "warn"
116 outputHandler = OutputHandler(self.log)
117 kp_kwargs = {
118 "processOutputLine": [outputHandler],
119 "universal_newlines": True,
122 valgrind = "valgrind"
123 if not os.path.exists(valgrind):
124 valgrind = findInPath(valgrind)
126 valgrind_args = [
127 valgrind,
128 "--sym-offsets=yes",
129 "--smc-check=all-non-file",
130 "--vex-iropt-register-updates=allregs-at-mem-access",
131 "--gen-suppressions=all",
132 "--num-callers=36",
133 "--leak-check=full",
134 "--show-possibly-lost=no",
135 "--track-origins=yes",
136 "--trace-children=yes",
137 "-v", # Enable verbosity to get the list of used suppressions
138 # Avoid excessive delays in the presence of spinlocks.
139 # See bug 1309851.
140 "--fair-sched=yes",
141 # Keep debuginfo after library unmap. See bug 1382280.
142 "--keep-debuginfo=yes",
143 # Reduce noise level on rustc and/or LLVM compiled code.
144 # See bug 1365915
145 "--expensive-definedness-checks=yes",
146 # Compensate for the compiler inlining `new` but not `delete`
147 # or vice versa.
148 "--show-mismatched-frees=no",
151 for s in suppressions:
152 valgrind_args.append("--suppressions=" + s)
154 supps_dir = os.path.join(build_dir, "valgrind")
155 supps_file1 = os.path.join(supps_dir, "cross-architecture.sup")
156 valgrind_args.append("--suppressions=" + supps_file1)
158 if mozinfo.os == "linux":
159 machtype = {
160 "x86_64": "x86_64-pc-linux-gnu",
161 "x86": "i386-pc-linux-gnu",
162 }.get(mozinfo.processor)
163 if machtype:
164 supps_file2 = os.path.join(supps_dir, machtype + ".sup")
165 if os.path.isfile(supps_file2):
166 valgrind_args.append("--suppressions=" + supps_file2)
168 exitcode = None
169 timeout = 1800
170 binary_not_found_exception = None
171 try:
172 runner = FirefoxRunner(
173 profile=profile,
174 binary=self.get_binary_path(),
175 cmdargs=firefox_args,
176 env=env,
177 process_args=kp_kwargs,
179 runner.start(debug_args=valgrind_args)
180 exitcode = runner.wait(timeout=timeout)
181 except BinaryNotFoundException as e:
182 binary_not_found_exception = e
183 finally:
184 errs = outputHandler.error_count
185 supps = outputHandler.suppression_count
186 if errs != supps:
187 status = 1 # turns the TBPL job orange
188 self.log(
189 logging.ERROR,
190 "valgrind-fail-parsing",
191 {"errs": errs, "supps": supps},
192 "TEST-UNEXPECTED-FAIL | valgrind-test | error parsing: {errs} errors "
193 "seen, but {supps} generated suppressions seen",
196 elif errs == 0:
197 status = 0
198 self.log(
199 logging.INFO,
200 "valgrind-pass",
202 "TEST-PASS | valgrind-test | valgrind found no errors",
204 else:
205 status = 1 # turns the TBPL job orange
206 # We've already printed details of the errors.
208 if binary_not_found_exception:
209 status = 2 # turns the TBPL job red
210 self.log(
211 logging.ERROR,
212 "valgrind-fail-errors",
213 {"error": str(binary_not_found_exception)},
214 "TEST-UNEXPECTED-FAIL | valgrind-test | {error}",
216 self.log(
217 logging.INFO,
218 "valgrind-fail-errors",
219 {"help": binary_not_found_exception.help()},
220 "{help}",
222 elif exitcode is None:
223 status = 2 # turns the TBPL job red
224 self.log(
225 logging.ERROR,
226 "valgrind-fail-timeout",
227 {"timeout": timeout},
228 "TEST-UNEXPECTED-FAIL | valgrind-test | Valgrind timed out "
229 "(reached {timeout} second limit)",
231 elif exitcode != 0:
232 status = 2 # turns the TBPL job red
233 self.log(
234 logging.ERROR,
235 "valgrind-fail-errors",
236 {"exitcode": exitcode},
237 "TEST-UNEXPECTED-FAIL | valgrind-test | non-zero exit code "
238 "from Valgrind: {exitcode}",
241 httpd.stop()
243 return status