Bug 1874684 - Part 6: Limit day length calculations to safe integers. r=mgaudet
[gecko.git] / build / valgrind / mach_commands.py
blobe48d5af311359fccd3a9eede5ced410663a0d102
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 import json
6 import logging
7 import os
8 import time
10 import mozinfo
11 from mach.decorators import Command, CommandArgument
12 from mozbuild.base import BinaryNotFoundException
13 from mozbuild.base import MachCommandConditions as conditions
16 def is_valgrind_build(cls):
17 """Must be a build with --enable-valgrind and --disable-jemalloc."""
18 defines = cls.config_environment.defines
19 return "MOZ_VALGRIND" in defines and "MOZ_MEMORY" not in defines
22 @Command(
23 "valgrind-test",
24 category="testing",
25 conditions=[conditions.is_firefox_or_thunderbird, is_valgrind_build],
26 description="Run the Valgrind test job (memory-related errors).",
28 @CommandArgument(
29 "--suppressions",
30 default=[],
31 action="append",
32 metavar="FILENAME",
33 help="Specify a suppression file for Valgrind to use. Use "
34 "--suppression multiple times to specify multiple suppression "
35 "files.",
37 def valgrind_test(command_context, suppressions):
38 """
39 Run Valgrind tests.
40 """
42 from mozfile import TemporaryDirectory
43 from mozhttpd import MozHttpd
44 from mozprofile import FirefoxProfile, Preferences
45 from mozprofile.permissions import ServerLocations
46 from mozrunner import FirefoxRunner
47 from mozrunner.utils import findInPath
48 from valgrind.output_handler import OutputHandler
50 build_dir = os.path.join(command_context.topsrcdir, "build")
52 # XXX: currently we just use the PGO inputs for Valgrind runs. This may
53 # change in the future.
54 httpd = MozHttpd(docroot=os.path.join(build_dir, "pgo"))
55 httpd.start(block=False)
57 with TemporaryDirectory() as profilePath:
58 # TODO: refactor this into mozprofile
59 profile_data_dir = os.path.join(
60 command_context.topsrcdir, "testing", "profiles"
62 with open(os.path.join(profile_data_dir, "profiles.json"), "r") as fh:
63 base_profiles = json.load(fh)["valgrind"]
65 prefpaths = [
66 os.path.join(profile_data_dir, profile, "user.js")
67 for profile in base_profiles
69 prefs = {}
70 for path in prefpaths:
71 prefs.update(Preferences.read_prefs(path))
73 interpolation = {
74 "server": "%s:%d" % httpd.httpd.server_address,
76 for k, v in prefs.items():
77 if isinstance(v, str):
78 v = v.format(**interpolation)
79 prefs[k] = Preferences.cast(v)
81 quitter = os.path.join(
82 command_context.topsrcdir, "tools", "quitter", "quitter@mozilla.org.xpi"
85 locations = ServerLocations()
86 locations.add_host(
87 host="127.0.0.1", port=httpd.httpd.server_port, options="primary"
90 profile = FirefoxProfile(
91 profile=profilePath,
92 preferences=prefs,
93 addons=[quitter],
94 locations=locations,
97 firefox_args = [httpd.get_url()]
99 env = os.environ.copy()
100 env["G_SLICE"] = "always-malloc"
101 env["MOZ_FORCE_DISABLE_E10S"] = "1"
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 outputHandler = OutputHandler(command_context.log)
108 kp_kwargs = {
109 "processOutputLine": [outputHandler],
110 "universal_newlines": True,
113 valgrind = "valgrind"
114 if not os.path.exists(valgrind):
115 valgrind = findInPath(valgrind)
117 valgrind_args = [
118 valgrind,
119 "--sym-offsets=yes",
120 "--smc-check=all-non-file",
121 "--vex-iropt-register-updates=allregs-at-mem-access",
122 "--gen-suppressions=all",
123 "--num-callers=36",
124 "--leak-check=full",
125 "--show-possibly-lost=no",
126 "--track-origins=yes",
127 "--trace-children=yes",
128 "--trace-children-skip=*/dbus-launch",
129 "-v", # Enable verbosity to get the list of used suppressions
130 # Avoid excessive delays in the presence of spinlocks.
131 # See bug 1309851.
132 "--fair-sched=yes",
133 # Keep debuginfo after library unmap. See bug 1382280.
134 "--keep-debuginfo=yes",
135 # Reduce noise level on rustc and/or LLVM compiled code.
136 # See bug 1365915
137 "--expensive-definedness-checks=yes",
138 # Compensate for the compiler inlining `new` but not `delete`
139 # or vice versa.
140 "--show-mismatched-frees=no",
143 for s in suppressions:
144 valgrind_args.append("--suppressions=" + s)
146 supps_dir = os.path.join(build_dir, "valgrind")
147 supps_file1 = os.path.join(supps_dir, "cross-architecture.sup")
148 valgrind_args.append("--suppressions=" + supps_file1)
150 if mozinfo.os == "linux":
151 machtype = {
152 "x86_64": "x86_64-pc-linux-gnu",
153 "x86": "i386-pc-linux-gnu",
154 }.get(mozinfo.processor)
155 if machtype:
156 supps_file2 = os.path.join(supps_dir, machtype + ".sup")
157 if os.path.isfile(supps_file2):
158 valgrind_args.append("--suppressions=" + supps_file2)
160 exitcode = None
161 timeout = 2400
162 binary_not_found_exception = None
163 try:
164 runner = FirefoxRunner(
165 profile=profile,
166 binary=command_context.get_binary_path(),
167 cmdargs=firefox_args,
168 env=env,
169 process_args=kp_kwargs,
171 start_time = time.monotonic()
172 runner.start(debug_args=valgrind_args)
173 exitcode = runner.wait(timeout=timeout)
174 end_time = time.monotonic()
175 if "MOZ_AUTOMATION" in os.environ:
176 data = {
177 "framework": {"name": "build_metrics"},
178 "suites": [
180 "name": "valgrind",
181 "value": end_time - start_time,
182 "lowerIsBetter": True,
183 "shouldAlert": False,
184 "subtests": [],
188 if "TASKCLUSTER_INSTANCE_TYPE" in os.environ:
189 # Include the instance type so results can be grouped.
190 data["suites"][0]["extraOptions"] = [
191 "taskcluster-%s" % os.environ["TASKCLUSTER_INSTANCE_TYPE"],
193 command_context.log(
194 logging.INFO,
195 "valgrind-perfherder",
196 {"data": json.dumps(data)},
197 "PERFHERDER_DATA: {data}",
199 except BinaryNotFoundException as e:
200 binary_not_found_exception = e
201 finally:
202 errs = outputHandler.error_count
203 supps = outputHandler.suppression_count
204 if errs != supps:
205 status = 1 # turns the TBPL job orange
206 command_context.log(
207 logging.ERROR,
208 "valgrind-fail-parsing",
209 {"errs": errs, "supps": supps},
210 "TEST-UNEXPECTED-FAIL | valgrind-test | error parsing: {errs} errors "
211 "seen, but {supps} generated suppressions seen",
214 elif errs == 0:
215 status = 0
216 command_context.log(
217 logging.INFO,
218 "valgrind-pass",
220 "TEST-PASS | valgrind-test | valgrind found no errors",
222 else:
223 status = 1 # turns the TBPL job orange
224 # We've already printed details of the errors.
226 if binary_not_found_exception:
227 status = 2 # turns the TBPL job red
228 command_context.log(
229 logging.ERROR,
230 "valgrind-fail-errors",
231 {"error": str(binary_not_found_exception)},
232 "TEST-UNEXPECTED-FAIL | valgrind-test | {error}",
234 command_context.log(
235 logging.INFO,
236 "valgrind-fail-errors",
237 {"help": binary_not_found_exception.help()},
238 "{help}",
240 elif exitcode is None:
241 status = 2 # turns the TBPL job red
242 command_context.log(
243 logging.ERROR,
244 "valgrind-fail-timeout",
245 {"timeout": timeout},
246 "TEST-UNEXPECTED-FAIL | valgrind-test | Valgrind timed out "
247 "(reached {timeout} second limit)",
249 elif exitcode != 0:
250 status = 2 # turns the TBPL job red
251 command_context.log(
252 logging.ERROR,
253 "valgrind-fail-errors",
254 {"exitcode": exitcode},
255 "TEST-UNEXPECTED-FAIL | valgrind-test | non-zero exit code "
256 "from Valgrind: {exitcode}",
259 httpd.stop()
261 return status