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
12 from mach
.decorators
import (
17 from mozbuild
.base
import (
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
31 class MachCommands(MachCommandBase
):
39 conditions
=[conditions
.is_firefox_or_thunderbird
, is_valgrind_build
],
40 description
="Run the Valgrind test job (memory-related errors).",
47 help="Specify a suppression file for Valgrind to use. Use "
48 "--suppression multiple times to specify multiple suppression "
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"]
76 os
.path
.join(profile_data_dir
, profile
, "user.js")
77 for profile
in base_profiles
80 for path
in prefpaths
:
81 prefs
.update(Preferences
.read_prefs(path
))
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()
97 host
="127.0.0.1", port
=httpd
.httpd
.server_port
, options
="primary"
100 profile
= FirefoxProfile(
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
)
118 "processOutputLine": [outputHandler
],
119 "universal_newlines": True,
122 valgrind
= "valgrind"
123 if not os
.path
.exists(valgrind
):
124 valgrind
= findInPath(valgrind
)
129 "--smc-check=all-non-file",
130 "--vex-iropt-register-updates=allregs-at-mem-access",
131 "--gen-suppressions=all",
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.
141 # Keep debuginfo after library unmap. See bug 1382280.
142 "--keep-debuginfo=yes",
143 # Reduce noise level on rustc and/or LLVM compiled code.
145 "--expensive-definedness-checks=yes",
146 # Compensate for the compiler inlining `new` but not `delete`
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":
160 "x86_64": "x86_64-pc-linux-gnu",
161 "x86": "i386-pc-linux-gnu",
162 }.get(mozinfo
.processor
)
164 supps_file2
= os
.path
.join(supps_dir
, machtype
+ ".sup")
165 if os
.path
.isfile(supps_file2
):
166 valgrind_args
.append("--suppressions=" + supps_file2
)
170 binary_not_found_exception
= None
172 runner
= FirefoxRunner(
174 binary
=self
.get_binary_path(),
175 cmdargs
=firefox_args
,
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
184 errs
= outputHandler
.error_count
185 supps
= outputHandler
.suppression_count
187 status
= 1 # turns the TBPL job orange
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",
202 "TEST-PASS | valgrind-test | valgrind found no errors",
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
212 "valgrind-fail-errors",
213 {"error": str(binary_not_found_exception
)},
214 "TEST-UNEXPECTED-FAIL | valgrind-test | {error}",
218 "valgrind-fail-errors",
219 {"help": binary_not_found_exception
.help()},
222 elif exitcode
is None:
223 status
= 2 # turns the TBPL job red
226 "valgrind-fail-timeout",
227 {"timeout": timeout
},
228 "TEST-UNEXPECTED-FAIL | valgrind-test | Valgrind timed out "
229 "(reached {timeout} second limit)",
232 status
= 2 # turns the TBPL job red
235 "valgrind-fail-errors",
236 {"exitcode": exitcode
},
237 "TEST-UNEXPECTED-FAIL | valgrind-test | non-zero exit code "
238 "from Valgrind: {exitcode}",