Bug 1354232 - Enable LSAN Leak detection in wpt, r=ahal,mccr8
authorJames Graham <james@hoppipolla.co.uk>
Wed, 16 May 2018 14:16:28 +0100
changeset 482442 e0d106c2112dd6845ca75414258ab74e5ed73d87
parent 482441 6d28db898070e56ac198dfa94553f83facd96ab7
child 482443 b8da0ef2695d8bca1c11b44447a71cef5ce5b3fc
push id9719
push userffxbld-merge
push dateFri, 24 Aug 2018 17:49:46 +0000
treeherdermozilla-beta@719ec98fba77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersahal, mccr8
bugs1354232
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1354232 - Enable LSAN Leak detection in wpt, r=ahal,mccr8 MozReview-Commit-ID: 1pPvYi5NQk8
testing/web-platform/moz.build
testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/firefox.py
--- a/testing/web-platform/moz.build
+++ b/testing/web-platform/moz.build
@@ -9,25 +9,29 @@ WEB_PLATFORM_TESTS_MANIFESTS += [
     ('mozilla/meta/MANIFEST.json', 'mozilla/tests/')
 ]
 
 TEST_HARNESS_FILES['web-platform'] += [
     'mach_commands_base.py',
     'mach_test_package_commands.py',
     'outbound/**',
     'runtests.py',
-    'wptrunner.ini'
+    'wptrunner.ini',
 ]
 
-TEST_HARNESS_FILES['web-platform'].certs = [
+TEST_HARNESS_FILES['web-platform'].certs += [
     'certs/cacert.pem',
     'certs/web-platform.test.key',
     'certs/web-platform.test.pem',
 ]
 
+TEST_HARNESS_FILES['web-platform'].prefs += [
+    '/build/sanitizers/lsan_suppressions.txt',
+]
+
 with Files("**"):
     SCHEDULES.exclusive = [
         'web-platform-tests',
         'web-platform-tests-reftests',
         'web-platform-tests-wdspec',
     ]
 
 with Files("moz.build"):
--- a/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/firefox.py
+++ b/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/firefox.py
@@ -5,17 +5,17 @@ import signal
 import subprocess
 import sys
 
 import mozinfo
 import mozleak
 from mozprocess import ProcessHandler
 from mozprofile import FirefoxProfile, Preferences
 from mozrunner import FirefoxRunner
-from mozrunner.utils import get_stack_fixer_function
+from mozrunner.utils import test_environment, get_stack_fixer_function
 from mozcrash import mozcrash
 
 from .base import (get_free_port,
                    Browser,
                    ExecutorBrowser,
                    require_arg,
                    cmd_arg,
                    browser_command)
@@ -75,16 +75,17 @@ def browser_kwargs(test_type, run_info_d
             "ca_certificate_path": kwargs["ssl_env"].ca_cert_path(),
             "e10s": kwargs["gecko_e10s"],
             "stackfix_dir": kwargs["stackfix_dir"],
             "binary_args": kwargs["binary_args"],
             "timeout_multiplier": get_timeout_multiplier(test_type,
                                                          run_info_data,
                                                          **kwargs),
             "leak_check": kwargs["leak_check"],
+            "asan": run_info_data.get("asan"),
             "stylo_threads": kwargs["stylo_threads"],
             "chaos_mode_flags": kwargs["chaos_mode_flags"],
             "config": kwargs["config"]}
 
 
 def executor_kwargs(test_type, server_config, cache_manager, run_info_data,
                     **kwargs):
     executor_kwargs = base_executor_kwargs(test_type, server_config,
@@ -146,18 +147,18 @@ def update_properties():
 class FirefoxBrowser(Browser):
     used_ports = set()
     init_timeout = 60
     shutdown_timeout = 60
 
     def __init__(self, logger, binary, prefs_root, test_type, extra_prefs=None, debug_info=None,
                  symbols_path=None, stackwalk_binary=None, certutil_binary=None,
                  ca_certificate_path=None, e10s=False, stackfix_dir=None,
-                 binary_args=None, timeout_multiplier=None, leak_check=False, stylo_threads=1,
-                 chaos_mode_flags=None, config=None):
+                 binary_args=None, timeout_multiplier=None, leak_check=False, asan=False,
+                 stylo_threads=1, chaos_mode_flags=None, config=None):
         Browser.__init__(self, logger)
         self.binary = binary
         self.prefs_root = prefs_root
         self.test_type = test_type
         self.extra_prefs = extra_prefs
         self.marionette_port = None
         self.runner = None
         self.debug_info = debug_info
@@ -173,33 +174,38 @@ class FirefoxBrowser(Browser):
             self.stack_fixer = get_stack_fixer_function(stackfix_dir,
                                                         self.symbols_path)
         else:
             self.stack_fixer = None
 
         if timeout_multiplier:
             self.init_timeout = self.init_timeout * timeout_multiplier
 
+        self.asan = asan
+        self.leak_check = leak_check
         self.leak_report_file = None
-        self.leak_check = leak_check
+        self.lsan_handler = None
+        if self.asan:
+            self.lsan_handler = mozleak.LSANLeaks(logger)
         self.stylo_threads = stylo_threads
         self.chaos_mode_flags = chaos_mode_flags
 
     def settings(self, test):
         return {"check_leaks": self.leak_check and not test.leaks}
 
     def start(self, **kwargs):
         if self.marionette_port is None:
             self.marionette_port = get_free_port(2828, exclude=self.used_ports)
             self.used_ports.add(self.marionette_port)
 
-        env = os.environ.copy()
-        env["MOZ_CRASHREPORTER"] = "1"
-        env["MOZ_CRASHREPORTER_SHUTDOWN"] = "1"
-        env["MOZ_DISABLE_NONLOCAL_CONNECTIONS"] = "1"
+        env = test_environment(xrePath=os.path.dirname(self.binary),
+                               debugger=self.debug_info is not None,
+                               log=self.logger,
+                               lsanPath=self.prefs_root)
+
         env["STYLO_THREADS"] = str(self.stylo_threads)
         if self.chaos_mode_flags is not None:
             env["MOZ_CHAOSMODE"] = str(self.chaos_mode_flags)
 
         preferences = self.load_prefs()
 
         self.profile = FirefoxProfile(preferences=preferences)
         self.profile.set_preferences({"marionette.port": self.marionette_port,
@@ -294,30 +300,31 @@ class FirefoxBrowser(Browser):
                             break
             except OSError:
                 # This can happen on Windows if the process is already dead
                 pass
         self.logger.debug("stopped")
 
     def process_leaks(self):
         self.logger.debug("PROCESS LEAKS %s" % self.leak_report_file)
-        if self.leak_report_file is None:
-            return
-        mozleak.process_leak_log(
-            self.leak_report_file,
-            leak_thresholds={
-                "default": 0,
-                "tab": 10000,  # See dependencies of bug 1051230.
-                # GMP rarely gets a log, but when it does, it leaks a little.
-                "geckomediaplugin": 20000,
-            },
-            ignore_missing_leaks=["geckomediaplugin"],
-            log=self.logger,
-            stack_fixer=self.stack_fixer
-        )
+        if self.lsan_handler:
+            self.lsan_handler.process()
+        if self.leak_report_file is not None:
+            mozleak.process_leak_log(
+                self.leak_report_file,
+                leak_thresholds={
+                    "default": 0,
+                    "tab": 10000,  # See dependencies of bug 1051230.
+                    # GMP rarely gets a log, but when it does, it leaks a little.
+                    "geckomediaplugin": 20000,
+                },
+                ignore_missing_leaks=["geckomediaplugin"],
+                log=self.logger,
+                stack_fixer=self.stack_fixer
+            )
 
     def pid(self):
         if self.runner.process_handler is None:
             return None
 
         try:
             return self.runner.process_handler.pid
         except AttributeError:
@@ -326,16 +333,18 @@ class FirefoxBrowser(Browser):
     def on_output(self, line):
         """Write a line of output from the firefox process to the log"""
         if "GLib-GObject-CRITICAL" in line:
             return
         if line:
             data = line.decode("utf8", "replace")
             if self.stack_fixer:
                 data = self.stack_fixer(data)
+            if self.lsan_handler:
+                self.lsan_handler.log(data)
             self.logger.process_output(self.pid(),
                                       data,
                                       command=" ".join(self.runner.command))
 
     def is_alive(self):
         if self.runner:
             return self.runner.is_running()
         return False