예제 #1
0
    def buildProfile(self, options):
        # preferences
        prefs = {}
        for path in self.preferences:
            prefs.update(Preferences.read_prefs(path))

        for v in options.extraPrefs:
            thispref = v.split("=", 1)
            if len(thispref) < 2:
                print "Error: syntax error in --setpref=" + v
                sys.exit(1)
            prefs[thispref[0]] = thispref[1]

        # interpolate the preferences
        interpolation = { "server": "%s:%s" % (options.webServer, options.httpPort),
                          "OOP": "true" if self.OOP else "false" }
        prefs = json.loads(json.dumps(prefs) % interpolation)

        self.profile = Profile(addons=self.getExtensionsToInstall(options),
                               apps=self.webapps,
                               locations=self.locations,
                               preferences=prefs,
                               proxy={"remote": options.webServer})

        options.profilePath = self.profile.profile
        # TODO bug 839108 - mozprofile should probably handle this
        manifest = self.addChromeToProfile(options)
        self.copyExtraFilesToProfile(options)
        return manifest
예제 #2
0
파일: b2g.py 프로젝트: html-shell/mozbuild
    def load_prefs(self):
        prefs_path = os.path.join(self.prefs_root, "prefs_general.js")
        if os.path.exists(prefs_path):
            preferences = Preferences.read_prefs(prefs_path)
        else:
            self.logger.warning("Failed to find base prefs file in %s" % prefs_path)
            preferences = []

        return preferences
예제 #3
0
    def build_profile(self, options):
        # preferences
        prefs = {}
        for path in self.preferences:
            prefs.update(Preferences.read_prefs(path))

        for v in options.extraPrefs:
            thispref = v.split("=", 1)
            if len(thispref) < 2:
                print "Error: syntax error in --setpref=" + v
                sys.exit(1)
            prefs[thispref[0]] = thispref[1]

        # interpolate the preferences
        interpolation = {
            "server": "%s:%s" %
            (options.webServer,
             options.httpPort),
            "OOP": "true" if self.out_of_process else "false"}
        prefs = json.loads(json.dumps(prefs) % interpolation)
        for pref in prefs:
            prefs[pref] = Preferences.cast(prefs[pref])

        kwargs = {
            'addons': self.getExtensionsToInstall(options),
            'apps': self.webapps,
            'locations': self.locations_file,
            'preferences': prefs,
            'proxy': {"remote": options.webServer}
        }

        if options.profile:
            self.profile = Profile.clone(options.profile, **kwargs)
        else:
            self.profile = Profile(**kwargs)

        options.profilePath = self.profile.profile
        # TODO bug 839108 - mozprofile should probably handle this
        manifest = self.addChromeToProfile(options)
        self.copyExtraFilesToProfile(options)
        return manifest
예제 #4
0
    def load_prefs(self):
        prefs = Preferences()

        pref_paths = []

        profiles = os.path.join(self.prefs_root, 'profiles.json')
        if os.path.isfile(profiles):
            with open(profiles, 'r') as fh:
                for name in json.load(fh)['web-platform-tests']:
                    if self.browser_channel in (None, 'nightly'):
                        pref_paths.append(os.path.join(self.prefs_root, name, 'user.js'))
                    elif name != 'unittest-features':
                        pref_paths.append(os.path.join(self.prefs_root, name, 'user.js'))
        else:
            # Old preference files used before the creation of profiles.json (remove when no longer supported)
            legacy_pref_paths = (
                os.path.join(self.prefs_root, 'prefs_general.js'),   # Used in Firefox 60 and below
                os.path.join(self.prefs_root, 'common', 'user.js'),  # Used in Firefox 61
            )
            for path in legacy_pref_paths:
                if os.path.isfile(path):
                    pref_paths.append(path)

        for path in pref_paths:
            if os.path.exists(path):
                prefs.add(Preferences.read_prefs(path))
            else:
                self.logger.warning("Failed to find base prefs file in %s" % path)

        # Add any custom preferences
        prefs.add(self.extra_prefs, cast=True)

        return prefs()
예제 #5
0
    def load_prefs(self):
        prefs = Preferences()

        pref_paths = []
        prefs_general = os.path.join(self.prefs_root, 'prefs_general.js')
        if os.path.isfile(prefs_general):
            # Old preference file used in Firefox 60 and earlier (remove when no longer supported)
            pref_paths.append(prefs_general)

        profiles = os.path.join(self.prefs_root, 'profiles.json')
        if os.path.isfile(profiles):
            with open(profiles, 'r') as fh:
                for name in json.load(fh)['web-platform-tests']:
                    pref_paths.append(os.path.join(self.prefs_root, name, 'user.js'))

        for path in pref_paths:
            if os.path.exists(path):
                prefs.add(Preferences.read_prefs(path))
            else:
                self.logger.warning("Failed to find base prefs file in %s" % path)

        # Add any custom preferences
        prefs.add(self.extra_prefs, cast=True)

        return prefs()
예제 #6
0
def create_mozprofile(profile_dir, application=None, test_type=None, env=None):
    # Ensure that the base `_temp/profiles/` directory exists before trying to
    # create a nested directory.
    if not os.path.exists(BASE_PROFILE_DIR):
        os.mkdir(BASE_PROFILE_DIR)

    if not profile_dir:
        full_profile_dir = mkdtemp(
            dir=BASE_PROFILE_DIR,
            prefix="fftool.",
            suffix=""
        )

    else:
        full_profile_dir = os.path.join(BASE_PROFILE_DIR, profile_dir)

        if os.path.exists(full_profile_dir):
            msg = "WARNING: Profile '{0}' already exists. Merging configs."
            Log.header(msg.format(full_profile_dir), 'XL', '-')

    prefs = Preferences()

    for path in prefs_paths(application, test_type, env):
        prefs.add_file(path)

    # Add the `fftool.profile.name` pref so we can go to about:config and see
    # what our current profile is.
    prefs.add([("fftool.profile.name", full_profile_dir)])

    profile = Profile(
        profile=full_profile_dir, restore=False, preferences=prefs())

    Log.header("Launching browser with the following user configs:")
    print(profile.summary())

    # this is the path to the created profile
    return full_profile_dir
예제 #7
0
    def load_prefs(self):
        prefs = Preferences()

        prefs_path = os.path.join(self.prefs_root, "prefs_general.js")
        if os.path.exists(prefs_path):
            prefs.add(Preferences.read_prefs(prefs_path))
        else:
            self.logger.warning("Failed to find base prefs file in %s" % prefs_path)

        # Add any custom preferences
        prefs.add(self.extra_prefs, cast=True)

        return prefs()
예제 #8
0
    def valgrind_test(self, suppressions):

        from mozfile import TemporaryDirectory
        from mozhttpd import MozHttpd
        from mozprofile import FirefoxProfile, Preferences
        from mozprofile.permissions import ServerLocations
        from mozrunner import FirefoxRunner
        from mozrunner.utils import findInPath
        from six import string_types
        from valgrind.output_handler import OutputHandler

        build_dir = os.path.join(self.topsrcdir, 'build')

        # XXX: currently we just use the PGO inputs for Valgrind runs.  This may
        # change in the future.
        httpd = MozHttpd(docroot=os.path.join(build_dir, 'pgo'))
        httpd.start(block=False)

        with TemporaryDirectory() as profilePath:
            # TODO: refactor this into mozprofile
            profile_data_dir = os.path.join(self.topsrcdir, 'testing',
                                            'profiles')
            with open(os.path.join(profile_data_dir, 'profiles.json'),
                      'r') as fh:
                base_profiles = json.load(fh)['valgrind']

            prefpaths = [
                os.path.join(profile_data_dir, profile, 'user.js')
                for profile in base_profiles
            ]
            prefs = {}
            for path in prefpaths:
                prefs.update(Preferences.read_prefs(path))

            interpolation = {
                'server': '%s:%d' % httpd.httpd.server_address,
            }
            for k, v in prefs.items():
                if isinstance(v, string_types):
                    v = v.format(**interpolation)
                prefs[k] = Preferences.cast(v)

            quitter = os.path.join(self.topsrcdir, 'tools', 'quitter',
                                   '*****@*****.**')

            locations = ServerLocations()
            locations.add_host(host='127.0.0.1',
                               port=httpd.httpd.server_port,
                               options='primary')

            profile = FirefoxProfile(profile=profilePath,
                                     preferences=prefs,
                                     addons=[quitter],
                                     locations=locations)

            firefox_args = [httpd.get_url()]

            env = os.environ.copy()
            env['G_SLICE'] = 'always-malloc'
            env['MOZ_CC_RUN_DURING_SHUTDOWN'] = '1'
            env['MOZ_CRASHREPORTER_NO_REPORT'] = '1'
            env['MOZ_DISABLE_NONLOCAL_CONNECTIONS'] = '1'
            env['XPCOM_DEBUG_BREAK'] = 'warn'

            env.update(self.extra_environment_variables)

            outputHandler = OutputHandler(self.log)
            kp_kwargs = {'processOutputLine': [outputHandler]}

            valgrind = 'valgrind'
            if not os.path.exists(valgrind):
                valgrind = findInPath(valgrind)

            valgrind_args = [
                valgrind,
                '--sym-offsets=yes',
                '--smc-check=all-non-file',
                '--vex-iropt-register-updates=allregs-at-mem-access',
                '--gen-suppressions=all',
                '--num-callers=36',
                '--leak-check=full',
                '--show-possibly-lost=no',
                '--track-origins=yes',
                '--trace-children=yes',
                '-v',  # Enable verbosity to get the list of used suppressions
                # Avoid excessive delays in the presence of spinlocks.
                # See bug 1309851.
                '--fair-sched=yes',
                # Keep debuginfo after library unmap.  See bug 1382280.
                '--keep-debuginfo=yes',
                # Reduce noise level on rustc and/or LLVM compiled code.
                # See bug 1365915
                '--expensive-definedness-checks=yes',
            ]

            for s in suppressions:
                valgrind_args.append('--suppressions=' + s)

            supps_dir = os.path.join(build_dir, 'valgrind')
            supps_file1 = os.path.join(supps_dir, 'cross-architecture.sup')
            valgrind_args.append('--suppressions=' + supps_file1)

            if mozinfo.os == 'linux':
                machtype = {
                    'x86_64': 'x86_64-pc-linux-gnu',
                    'x86': 'i386-pc-linux-gnu',
                }.get(mozinfo.processor)
                if machtype:
                    supps_file2 = os.path.join(supps_dir, machtype + '.sup')
                    if os.path.isfile(supps_file2):
                        valgrind_args.append('--suppressions=' + supps_file2)

            exitcode = None
            timeout = 1800
            try:
                runner = FirefoxRunner(profile=profile,
                                       binary=self.get_binary_path(),
                                       cmdargs=firefox_args,
                                       env=env,
                                       process_args=kp_kwargs)
                runner.start(debug_args=valgrind_args)
                exitcode = runner.wait(timeout=timeout)

            finally:
                errs = outputHandler.error_count
                supps = outputHandler.suppression_count
                if errs != supps:
                    status = 1  # turns the TBPL job orange
                    self.log(
                        logging.ERROR, 'valgrind-fail-parsing', {
                            'errs': errs,
                            'supps': supps
                        },
                        'TEST-UNEXPECTED-FAIL | valgrind-test | error parsing: {errs} errors '
                        'seen, but {supps} generated suppressions seen')

                elif errs == 0:
                    status = 0
                    self.log(
                        logging.INFO, 'valgrind-pass', {},
                        'TEST-PASS | valgrind-test | valgrind found no errors')
                else:
                    status = 1  # turns the TBPL job orange
                    # We've already printed details of the errors.

                if exitcode is None:
                    status = 2  # turns the TBPL job red
                    self.log(
                        logging.ERROR, 'valgrind-fail-timeout',
                        {'timeout': timeout},
                        'TEST-UNEXPECTED-FAIL | valgrind-test | Valgrind timed out '
                        '(reached {timeout} second limit)')
                elif exitcode != 0:
                    status = 2  # turns the TBPL job red
                    self.log(
                        logging.ERROR, 'valgrind-fail-errors',
                        {'exitcode': exitcode},
                        'TEST-UNEXPECTED-FAIL | valgrind-test | non-zero exit code '
                        'from Valgrind: {exitcode}')

                httpd.stop()

            return status
예제 #9
0
    httpd = MozHttpd(port=PORT,
                     docroot=os.path.join(build.topsrcdir, "build", "pgo"),
                     path_mappings=path_mappings)
    httpd.start(block=False)

    locations = ServerLocations()
    locations.add_host(host='127.0.0.1',
                       port=PORT,
                       options='primary,privileged')

    with TemporaryDirectory() as profilePath:
        # TODO: refactor this into mozprofile
        prefpath = os.path.join(build.topsrcdir, "testing", "profiles",
                                "prefs_general.js")
        prefs = {}
        prefs.update(Preferences.read_prefs(prefpath))
        interpolation = {
            "server": "%s:%d" % httpd.httpd.server_address,
            "OOP": "false"
        }
        prefs = json.loads(json.dumps(prefs) % interpolation)
        for pref in prefs:
            prefs[pref] = Preferences.cast(prefs[pref])
        profile = FirefoxProfile(profile=profilePath,
                                 preferences=prefs,
                                 addons=[
                                     os.path.join(build.topsrcdir, 'tools',
                                                  'quitter',
                                                  '*****@*****.**')
                                 ],
                                 locations=locations)
예제 #10
0
    old_profraw_files = glob.glob('*.profraw')
    for f in old_profraw_files:
        os.remove(f)

    with TemporaryDirectory() as profilePath:
        # TODO: refactor this into mozprofile
        profile_data_dir = os.path.join(build.topsrcdir, 'testing', 'profiles')
        with open(os.path.join(profile_data_dir, 'profiles.json'), 'r') as fh:
            base_profiles = json.load(fh)['profileserver']

        prefpaths = [os.path.join(profile_data_dir, profile, 'user.js')
                     for profile in base_profiles]

        prefs = {}
        for path in prefpaths:
            prefs.update(Preferences.read_prefs(path))

        interpolation = {"server": "%s:%d" % httpd.httpd.server_address}
        for k, v in prefs.items():
            if isinstance(v, string_types):
                v = v.format(**interpolation)
            prefs[k] = Preferences.cast(v)

        # Enforce e10s. This isn't in one of the user.js files because those
        # are shared with android, which doesn't want this on. We can't
        # interpolate because the formatting code only works for strings,
        # and this is a bool pref.
        prefs["browser.tabs.remote.autostart"] = True

        profile = FirefoxProfile(profile=profilePath,
                                 preferences=prefs,
예제 #11
0
    def run_tests(self):
        """
        Generate the PGO profile data
        """
        from mozhttpd import MozHttpd
        from mozprofile import Preferences
        from mozdevice import ADBDevice, ADBTimeoutError
        from six import string_types
        from marionette_driver.marionette import Marionette

        app = self.query_package_name()

        IP = '10.0.2.2'
        PORT = 8888

        PATH_MAPPINGS = {
            '/js-input/webkit/PerformanceTests':
            'third_party/webkit/PerformanceTests',
        }

        dirs = self.query_abs_dirs()
        topsrcdir = os.path.join(dirs['abs_work_dir'], 'src')
        adb = self.query_exe('adb')

        path_mappings = {
            k: os.path.join(topsrcdir, v)
            for k, v in PATH_MAPPINGS.items()
        }
        httpd = MozHttpd(port=PORT,
                         docroot=os.path.join(topsrcdir, "build", "pgo"),
                         path_mappings=path_mappings)
        httpd.start(block=False)

        profile_data_dir = os.path.join(topsrcdir, 'testing', 'profiles')
        with open(os.path.join(profile_data_dir, 'profiles.json'), 'r') as fh:
            base_profiles = json.load(fh)['profileserver']

        prefpaths = [
            os.path.join(profile_data_dir, profile, 'user.js')
            for profile in base_profiles
        ]

        prefs = {}
        for path in prefpaths:
            prefs.update(Preferences.read_prefs(path))

        interpolation = {
            "server": "%s:%d" % httpd.httpd.server_address,
            "OOP": "false"
        }
        for k, v in prefs.items():
            if isinstance(v, string_types):
                v = v.format(**interpolation)
            prefs[k] = Preferences.cast(v)

        # Enforce 1proc. This isn't in one of the user.js files because those
        # are shared with desktop, which wants e10s. We can't interpolate
        # because the formatting code only works for strings, and this is a
        # bool pref.
        prefs["browser.tabs.remote.autostart"] = False

        outputdir = self.config.get('output_directory', '/sdcard/pgo_profile')
        jarlog = posixpath.join(outputdir, 'en-US.log')
        profdata = posixpath.join(outputdir, 'default_%p_random_%m.profraw')

        env = {}
        env["XPCOM_DEBUG_BREAK"] = "warn"
        env["MOZ_IN_AUTOMATION"] = "1"
        env["MOZ_JAR_LOG_FILE"] = jarlog
        env["LLVM_PROFILE_FILE"] = profdata

        adbdevice = ADBDevice(adb=adb, device='emulator-5554')
        adbdevice.mkdir(outputdir)

        try:
            # Run Fennec a first time to initialize its profile
            driver = Marionette(
                app='fennec',
                package_name=app,
                adb_path=adb,
                bin="target.apk",
                prefs=prefs,
                connect_to_running_emulator=True,
                startup_timeout=1000,
                env=env,
            )
            driver.start_session()

            # Now generate the profile and wait for it to complete
            for page in PAGES:
                driver.navigate("http://%s:%d/%s" % (IP, PORT, page))
                timeout = 2
                if 'Speedometer/index.html' in page:
                    # The Speedometer test actually runs many tests internally in
                    # javascript, so it needs extra time to run through them. The
                    # emulator doesn't get very far through the whole suite, but
                    # this extra time at least lets some of them process.
                    timeout = 360
                time.sleep(timeout)

            driver.set_context("chrome")
            driver.execute_script("""
                Components.utils.import("resource://gre/modules/Services.jsm");
                let cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"]
                    .createInstance(Components.interfaces.nsISupportsPRBool);
                Services.obs.notifyObservers(cancelQuit, "quit-application-requested", null);
                return cancelQuit.data;
            """)
            driver.execute_script("""
                Components.utils.import("resource://gre/modules/Services.jsm");
                Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit)
            """)

            # There is a delay between execute_script() returning and the profile data
            # actually getting written out, so poll the device until we get a profile.
            for i in range(50):
                if not adbdevice.process_exist(app):
                    break
                time.sleep(2)
            else:
                raise Exception("Android App (%s) never quit" % app)

            # Pull all the profraw files and en-US.log
            adbdevice.pull(outputdir, '/builds/worker/workspace/')
        except ADBTimeoutError:
            self.fatal('INFRA-ERROR: Failed with an ADBTimeoutError',
                       EXIT_STATUS_DICT[TBPL_RETRY])

        profraw_files = glob.glob('/builds/worker/workspace/*.profraw')
        if not profraw_files:
            self.fatal(
                'Could not find any profraw files in /builds/worker/workspace')
        merge_cmd = [
            os.path.join(os.environ['MOZ_FETCHES_DIR'],
                         'clang/bin/llvm-profdata'),
            'merge',
            '-o',
            '/builds/worker/workspace/merged.profdata',
        ] + profraw_files
        rc = subprocess.call(merge_cmd)
        if rc != 0:
            self.fatal(
                'INFRA-ERROR: Failed to merge profile data. Corrupt profile?',
                EXIT_STATUS_DICT[TBPL_RETRY])

        # tarfile doesn't support xz in this version of Python
        tar_cmd = [
            'tar',
            '-acvf',
            '/builds/worker/artifacts/profdata.tar.xz',
            '-C',
            '/builds/worker/workspace',
            'merged.profdata',
            'en-US.log',
        ]
        subprocess.check_call(tar_cmd)

        httpd.stop()
예제 #12
0
    def run_tests(self):
        """
        Generate the PGO profile data
        """
        from mozhttpd import MozHttpd
        from mozprofile import Preferences
        from mozdevice import ADBDeviceFactory, ADBTimeoutError
        from six import string_types
        from marionette_driver.marionette import Marionette

        app = self.query_package_name()

        IP = "10.0.2.2"
        PORT = 8888

        PATH_MAPPINGS = {
            "/js-input/webkit/PerformanceTests":
            "third_party/webkit/PerformanceTests",
        }

        dirs = self.query_abs_dirs()
        topsrcdir = dirs["abs_src_dir"]
        adb = self.query_exe("adb")

        path_mappings = {
            k: os.path.join(topsrcdir, v)
            for k, v in PATH_MAPPINGS.items()
        }
        httpd = MozHttpd(
            port=PORT,
            docroot=os.path.join(topsrcdir, "build", "pgo"),
            path_mappings=path_mappings,
        )
        httpd.start(block=False)

        profile_data_dir = os.path.join(topsrcdir, "testing", "profiles")
        with open(os.path.join(profile_data_dir, "profiles.json"), "r") as fh:
            base_profiles = json.load(fh)["profileserver"]

        prefpaths = [
            os.path.join(profile_data_dir, profile, "user.js")
            for profile in base_profiles
        ]

        prefs = {}
        for path in prefpaths:
            prefs.update(Preferences.read_prefs(path))

        interpolation = {
            "server": "%s:%d" % httpd.httpd.server_address,
            "OOP": "false"
        }
        for k, v in prefs.items():
            if isinstance(v, string_types):
                v = v.format(**interpolation)
            prefs[k] = Preferences.cast(v)

        outputdir = self.config.get("output_directory", "/sdcard/pgo_profile")
        jarlog = posixpath.join(outputdir, "en-US.log")
        profdata = posixpath.join(outputdir, "default_%p_random_%m.profraw")

        env = {}
        env["XPCOM_DEBUG_BREAK"] = "warn"
        env["MOZ_IN_AUTOMATION"] = "1"
        env["MOZ_JAR_LOG_FILE"] = jarlog
        env["LLVM_PROFILE_FILE"] = profdata

        if self.query_minidump_stackwalk():
            os.environ["MINIDUMP_STACKWALK"] = self.minidump_stackwalk_path
        os.environ["MINIDUMP_SAVE_PATH"] = self.query_abs_dirs(
        )["abs_blob_upload_dir"]
        if not self.symbols_path:
            self.symbols_path = os.environ.get("MOZ_FETCHES_DIR")

        # Force test_root to be on the sdcard for android pgo
        # builds which fail for Android 4.3 when profiles are located
        # in /data/local/tmp/test_root with
        # E AndroidRuntime: FATAL EXCEPTION: Gecko
        # E AndroidRuntime: java.lang.IllegalArgumentException: \
        #    Profile directory must be writable if specified: /data/local/tmp/test_root/profile
        # This occurs when .can-write-sentinel is written to
        # the profile in
        # mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoProfile.java.
        # This is not a problem on later versions of Android. This
        # over-ride of test_root should be removed when Android 4.3 is no
        # longer supported.
        sdcard_test_root = "/sdcard/test_root"
        adbdevice = ADBDeviceFactory(adb=adb,
                                     device="emulator-5554",
                                     test_root=sdcard_test_root)
        if adbdevice.test_root != sdcard_test_root:
            # If the test_root was previously set and shared
            # the initializer will not have updated the shared
            # value. Force it to match the sdcard_test_root.
            adbdevice.test_root = sdcard_test_root
        adbdevice.mkdir(outputdir, parents=True)

        try:
            # Run Fennec a first time to initialize its profile
            driver = Marionette(
                app="fennec",
                package_name=app,
                adb_path=adb,
                bin="geckoview-androidTest.apk",
                prefs=prefs,
                connect_to_running_emulator=True,
                startup_timeout=1000,
                env=env,
                symbols_path=self.symbols_path,
            )
            driver.start_session()

            # Now generate the profile and wait for it to complete
            for page in PAGES:
                driver.navigate("http://%s:%d/%s" % (IP, PORT, page))
                timeout = 2
                if "Speedometer/index.html" in page:
                    # The Speedometer test actually runs many tests internally in
                    # javascript, so it needs extra time to run through them. The
                    # emulator doesn't get very far through the whole suite, but
                    # this extra time at least lets some of them process.
                    timeout = 360
                time.sleep(timeout)

            driver.set_context("chrome")
            driver.execute_script("""
                Components.utils.import("resource://gre/modules/Services.jsm");
                let cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"]
                    .createInstance(Components.interfaces.nsISupportsPRBool);
                Services.obs.notifyObservers(cancelQuit, "quit-application-requested", null);
                return cancelQuit.data;
            """)
            driver.execute_script("""
                Components.utils.import("resource://gre/modules/Services.jsm");
                Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit)
            """)

            # There is a delay between execute_script() returning and the profile data
            # actually getting written out, so poll the device until we get a profile.
            for i in range(50):
                if not adbdevice.process_exist(app):
                    break
                time.sleep(2)
            else:
                raise Exception("Android App (%s) never quit" % app)

            # Pull all the profraw files and en-US.log
            adbdevice.pull(outputdir, "/builds/worker/workspace/")
        except ADBTimeoutError:
            self.fatal(
                "INFRA-ERROR: Failed with an ADBTimeoutError",
                EXIT_STATUS_DICT[TBPL_RETRY],
            )

        profraw_files = glob.glob("/builds/worker/workspace/*.profraw")
        if not profraw_files:
            self.fatal(
                "Could not find any profraw files in /builds/worker/workspace")
        merge_cmd = [
            os.path.join(os.environ["MOZ_FETCHES_DIR"],
                         "clang/bin/llvm-profdata"),
            "merge",
            "-o",
            "/builds/worker/workspace/merged.profdata",
        ] + profraw_files
        rc = subprocess.call(merge_cmd)
        if rc != 0:
            self.fatal(
                "INFRA-ERROR: Failed to merge profile data. Corrupt profile?",
                EXIT_STATUS_DICT[TBPL_RETRY],
            )

        # tarfile doesn't support xz in this version of Python
        tar_cmd = [
            "tar",
            "-acvf",
            "/builds/worker/artifacts/profdata.tar.xz",
            "-C",
            "/builds/worker/workspace",
            "merged.profdata",
            "en-US.log",
        ]
        subprocess.check_call(tar_cmd)

        httpd.stop()
예제 #13
0
    httpd.start(block=False)

    locations = ServerLocations()
    locations.add_host(host='127.0.0.1',
                       port=PORT,
                       options='primary,privileged')

    with TemporaryDirectory() as profilePath:
        # TODO: refactor this into mozprofile
        prefpath = os.path.join(build.topsrcdir, "testing", "profiles",
                                "common", "user.js")
        overridepath = os.path.join(build.topsrcdir, "build", "pgo",
                                    "prefs_override.js")

        prefs = {}
        prefs.update(Preferences.read_prefs(prefpath))
        prefs.update(Preferences.read_prefs(overridepath))

        interpolation = {
            "server": "%s:%d" % httpd.httpd.server_address,
            "OOP": "false"
        }
        for k, v in prefs.items():
            if isinstance(v, string_types):
                v = v.format(**interpolation)
            prefs[k] = Preferences.cast(v)

        profile = FirefoxProfile(profile=profilePath,
                                 preferences=prefs,
                                 addons=[
                                     os.path.join(build.topsrcdir, 'tools',
예제 #14
0
  httpd = MozHttpd(port=PORT,
                   docroot=os.path.join(build.topsrcdir, "build", "pgo"))
  httpd.start(block=False)

  locations = ServerLocations()
  locations.add_host(host='127.0.0.1',
                     port=PORT,
                     options='primary,privileged')

  #TODO: mozfile.TemporaryDirectory
  profilePath = tempfile.mkdtemp()
  try:
    #TODO: refactor this into mozprofile
    prefpath = os.path.join(build.topsrcdir, "testing", "profiles", "prefs_general.js")
    prefs = {}
    prefs.update(Preferences.read_prefs(prefpath))
    interpolation = { "server": "%s:%d" % httpd.httpd.server_address,
                      "OOP": "false"}
    prefs = json.loads(json.dumps(prefs) % interpolation)
    for pref in prefs:
      prefs[pref] = Preferences.cast(prefs[pref])
    profile = FirefoxProfile(profile=profilePath,
                             preferences=prefs,
                             addons=[os.path.join(build.distdir, 'xpi-stage', 'quitter')],
                             locations=locations)

    env = os.environ.copy()
    env["MOZ_CRASHREPORTER_NO_REPORT"] = "1"
    env["XPCOM_DEBUG_BREAK"] = "warn"

    # For VC12, make sure we can find the right bitness of pgort120.dll
예제 #15
0
    def run_tests(self):
        """
        Generate the PGO profile data
        """
        from mozhttpd import MozHttpd
        from mozprofile import Preferences
        from mozdevice import ADBDevice, ADBProcessError, ADBTimeoutError
        from six import string_types
        from marionette_driver.marionette import Marionette

        app = self.query_package_name()

        IP = '10.0.2.2'
        PORT = 8888

        PATH_MAPPINGS = {
            '/js-input/webkit/PerformanceTests':
            'third_party/webkit/PerformanceTests',
        }

        dirs = self.query_abs_dirs()
        topsrcdir = os.path.join(dirs['abs_work_dir'], 'src')
        adb = self.query_exe('adb')

        path_mappings = {
            k: os.path.join(topsrcdir, v)
            for k, v in PATH_MAPPINGS.items()
        }
        httpd = MozHttpd(port=PORT,
                         docroot=os.path.join(topsrcdir, "build", "pgo"),
                         path_mappings=path_mappings)
        httpd.start(block=False)

        profile_data_dir = os.path.join(topsrcdir, 'testing', 'profiles')
        with open(os.path.join(profile_data_dir, 'profiles.json'), 'r') as fh:
            base_profiles = json.load(fh)['profileserver']

        prefpaths = [
            os.path.join(profile_data_dir, profile, 'user.js')
            for profile in base_profiles
        ]

        prefs = {}
        for path in prefpaths:
            prefs.update(Preferences.read_prefs(path))

        interpolation = {
            "server": "%s:%d" % httpd.httpd.server_address,
            "OOP": "false"
        }
        for k, v in prefs.items():
            if isinstance(v, string_types):
                v = v.format(**interpolation)
            prefs[k] = Preferences.cast(v)

        outputdir = self.config.get('output_directory', '/sdcard')
        jarlog = posixpath.join(outputdir, 'en-US.log')
        profdata = posixpath.join(outputdir, 'default.profraw')

        env = {}
        env["XPCOM_DEBUG_BREAK"] = "warn"
        env["MOZ_IN_AUTOMATION"] = "1"
        env["MOZ_JAR_LOG_FILE"] = jarlog
        env["LLVM_PROFILE_FILE"] = profdata

        adbdevice = ADBDevice(adb=adb, device='emulator-5554')

        try:
            # Run Fennec a first time to initialize its profile
            driver = Marionette(
                app='fennec',
                package_name=app,
                adb_path=adb,
                bin="target.apk",
                prefs=prefs,
                connect_to_running_emulator=True,
                startup_timeout=1000,
                env=env,
            )
            driver.start_session()

            # Now generate the profile and wait for it to complete
            for page in PAGES:
                driver.navigate("http://%s:%d/%s" % (IP, PORT, page))
                timeout = 2
                if 'Speedometer/index.html' in page:
                    # The Speedometer test actually runs many tests internally in
                    # javascript, so it needs extra time to run through them. The
                    # emulator doesn't get very far through the whole suite, but
                    # this extra time at least lets some of them process.
                    timeout = 360
                time.sleep(timeout)

            driver.set_context("chrome")
            driver.execute_script("""
                Components.utils.import("resource://gre/modules/Services.jsm");
                let cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"]
                    .createInstance(Components.interfaces.nsISupportsPRBool);
                Services.obs.notifyObservers(cancelQuit, "quit-application-requested", null);
                return cancelQuit.data;
            """)
            driver.execute_script("""
                Components.utils.import("resource://gre/modules/Services.jsm");
                Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit)
            """)

            # There is a delay between execute_script() returning and the profile data
            # actually getting written out, so poll the device until we get a profile.
            for i in range(50):
                try:
                    localprof = '/builds/worker/workspace/default.profraw'
                    adbdevice.pull(profdata, localprof)
                    stats = os.stat(localprof)
                    if stats.st_size == 0:
                        # The file may not have been fully written yet, so retry until we
                        # get actual data.
                        time.sleep(2)
                    else:
                        break
                except ADBProcessError:
                    # The file may not exist at all yet, which would raise an
                    # ADBProcessError, so retry.
                    time.sleep(2)
            else:
                raise Exception("Unable to pull default.profraw")
            adbdevice.pull(jarlog, '/builds/worker/workspace/en-US.log')
        except ADBTimeoutError:
            self.fatal('INFRA-ERROR: Failed with an ADBTimeoutError',
                       EXIT_STATUS_DICT[TBPL_RETRY])

        # We normally merge as part of a GENERATED_FILES step in the profile-use
        # build, but Android runs sometimes result in a truncated profile. We do
        # a merge here to make sure the data isn't corrupt so we can retry the
        # 'run' task if necessary.
        merge_cmd = [
            '/builds/worker/workspace/build/clang/bin/llvm-profdata',
            'merge',
            '/builds/worker/workspace/default.profraw',
            '-o',
            '/builds/worker/workspace/merged.profraw',
        ]
        rc = subprocess.call(merge_cmd)
        if rc != 0:
            self.fatal(
                'INFRA-ERROR: Failed to merge profile data. Corrupt profile?',
                EXIT_STATUS_DICT[TBPL_RETRY])

        # tarfile doesn't support xz in this version of Python
        tar_cmd = [
            'tar',
            '-acvf',
            '/builds/worker/artifacts/profdata.tar.xz',
            '-C',
            '/builds/worker/workspace',
            'merged.profraw',
            'en-US.log',
        ]
        subprocess.check_call(tar_cmd)

        httpd.stop()
예제 #16
0
    def valgrind_test(self, suppressions):
        import json
        import sys
        import tempfile

        from mozbuild.base import MozbuildObject
        from mozfile import TemporaryDirectory
        from mozhttpd import MozHttpd
        from mozprofile import FirefoxProfile, Preferences
        from mozprofile.permissions import ServerLocations
        from mozrunner import FirefoxRunner
        from mozrunner.utils import findInPath
        from valgrind.output_handler import OutputHandler

        build_dir = os.path.join(self.topsrcdir, 'build')

        # XXX: currently we just use the PGO inputs for Valgrind runs.  This may
        # change in the future.
        httpd = MozHttpd(docroot=os.path.join(build_dir, 'pgo'))
        httpd.start(block=False)

        with TemporaryDirectory() as profilePath:
            #TODO: refactor this into mozprofile
            prefpath = os.path.join(self.topsrcdir, 'testing', 'profiles', 'prefs_general.js')
            prefs = {}
            prefs.update(Preferences.read_prefs(prefpath))
            interpolation = { 'server': '%s:%d' % httpd.httpd.server_address,
                              'OOP': 'false'}
            prefs = json.loads(json.dumps(prefs) % interpolation)
            for pref in prefs:
                prefs[pref] = Preferences.cast(prefs[pref])

            quitter = os.path.join(self.topsrcdir, 'tools', 'quitter', '*****@*****.**')

            locations = ServerLocations()
            locations.add_host(host='127.0.0.1',
                               port=httpd.httpd.server_port,
                               options='primary')

            profile = FirefoxProfile(profile=profilePath,
                                     preferences=prefs,
                                     addons=[quitter],
                                     locations=locations)

            firefox_args = [httpd.get_url()]

            env = os.environ.copy()
            env['G_SLICE'] = 'always-malloc'
            env['MOZ_CC_RUN_DURING_SHUTDOWN'] = '1'
            env['MOZ_CRASHREPORTER_NO_REPORT'] = '1'
            env['XPCOM_DEBUG_BREAK'] = 'warn'

            env.update(self.extra_environment_variables)

            outputHandler = OutputHandler(self.log)
            kp_kwargs = {'processOutputLine': [outputHandler]}

            valgrind = 'valgrind'
            if not os.path.exists(valgrind):
                valgrind = findInPath(valgrind)

            valgrind_args = [
                valgrind,
                '--smc-check=all-non-file',
                '--vex-iropt-register-updates=allregs-at-mem-access',
                '--gen-suppressions=all',
                '--num-callers=36',
                '--leak-check=full',
                '--show-possibly-lost=no',
                '--track-origins=yes',
                '--trace-children=yes',
                '-v',  # Enable verbosity to get the list of used suppressions
            ]

            for s in suppressions:
                valgrind_args.append('--suppressions=' + s)

            supps_dir = os.path.join(build_dir, 'valgrind')
            supps_file1 = os.path.join(supps_dir, 'cross-architecture.sup')
            valgrind_args.append('--suppressions=' + supps_file1)

            # MACHTYPE is an odd bash-only environment variable that doesn't
            # show up in os.environ, so we have to get it another way.
            machtype = subprocess.check_output(['bash', '-c', 'echo $MACHTYPE']).rstrip()
            supps_file2 = os.path.join(supps_dir, machtype + '.sup')
            if os.path.isfile(supps_file2):
                valgrind_args.append('--suppressions=' + supps_file2)

            exitcode = None
            timeout = 1800
            try:
                runner = FirefoxRunner(profile=profile,
                                       binary=self.get_binary_path(),
                                       cmdargs=firefox_args,
                                       env=env,
                                       process_args=kp_kwargs)
                runner.start(debug_args=valgrind_args)
                exitcode = runner.wait(timeout=timeout)

            finally:
                errs = outputHandler.error_count
                supps = outputHandler.suppression_count
                if errs != supps:
                    status = 1  # turns the TBPL job orange
                    self.log(logging.ERROR, 'valgrind-fail-parsing',
                             {'errs': errs, 'supps': supps},
                             'TEST-UNEXPECTED-FAIL | valgrind-test | error parsing: {errs} errors seen, but {supps} generated suppressions seen')

                elif errs == 0:
                    status = 0
                    self.log(logging.INFO, 'valgrind-pass', {},
                             'TEST-PASS | valgrind-test | valgrind found no errors')
                else:
                    status = 1  # turns the TBPL job orange
                    # We've already printed details of the errors.

                if exitcode == None:
                    status = 2  # turns the TBPL job red
                    self.log(logging.ERROR, 'valgrind-fail-timeout',
                             {'timeout': timeout},
                             'TEST-UNEXPECTED-FAIL | valgrind-test | Valgrind timed out (reached {timeout} second limit)')
                elif exitcode != 0:
                    status = 2  # turns the TBPL job red
                    self.log(logging.ERROR, 'valgrind-fail-errors', {},
                             'TEST-UNEXPECTED-FAIL | valgrind-test | non-zero exit code from Valgrind')

                httpd.stop()

            return status
예제 #17
0
    def valgrind_test(self, suppressions):
        import json
        import sys
        import tempfile

        from mozbuild.base import MozbuildObject
        from mozfile import TemporaryDirectory
        from mozhttpd import MozHttpd
        from mozprofile import FirefoxProfile, Preferences
        from mozprofile.permissions import ServerLocations
        from mozrunner import FirefoxRunner
        from mozrunner.utils import findInPath
        from valgrind.output_handler import OutputHandler

        build_dir = os.path.join(self.topsrcdir, "build")

        # XXX: currently we just use the PGO inputs for Valgrind runs.  This may
        # change in the future.
        httpd = MozHttpd(docroot=os.path.join(build_dir, "pgo"))
        httpd.start(block=False)

        with TemporaryDirectory() as profilePath:
            # TODO: refactor this into mozprofile
            prefpath = os.path.join(self.topsrcdir, "testing", "profiles", "prefs_general.js")
            prefs = {}
            prefs.update(Preferences.read_prefs(prefpath))
            interpolation = {"server": "%s:%d" % httpd.httpd.server_address, "OOP": "false"}
            prefs = json.loads(json.dumps(prefs) % interpolation)
            for pref in prefs:
                prefs[pref] = Preferences.cast(prefs[pref])

            quitter = os.path.join(self.topsrcdir, "tools", "quitter", "*****@*****.**")

            locations = ServerLocations()
            locations.add_host(host="127.0.0.1", port=httpd.httpd.server_port, options="primary")

            profile = FirefoxProfile(profile=profilePath, preferences=prefs, addons=[quitter], locations=locations)

            firefox_args = [httpd.get_url()]

            env = os.environ.copy()
            env["G_SLICE"] = "always-malloc"
            env["MOZ_CC_RUN_DURING_SHUTDOWN"] = "1"
            env["MOZ_CRASHREPORTER_NO_REPORT"] = "1"
            env["XPCOM_DEBUG_BREAK"] = "warn"

            env.update(self.extra_environment_variables)

            outputHandler = OutputHandler(self.log)
            kp_kwargs = {"processOutputLine": [outputHandler]}

            valgrind = "valgrind"
            if not os.path.exists(valgrind):
                valgrind = findInPath(valgrind)

            valgrind_args = [
                valgrind,
                "--smc-check=all-non-file",
                "--vex-iropt-register-updates=allregs-at-mem-access",
                "--gen-suppressions=all",
                "--num-callers=36",
                "--leak-check=full",
                "--show-possibly-lost=no",
                "--track-origins=yes",
                "--trace-children=yes",
                "-v",  # Enable verbosity to get the list of used suppressions
            ]

            for s in suppressions:
                valgrind_args.append("--suppressions=" + s)

            supps_dir = os.path.join(build_dir, "valgrind")
            supps_file1 = os.path.join(supps_dir, "cross-architecture.sup")
            valgrind_args.append("--suppressions=" + supps_file1)

            # MACHTYPE is an odd bash-only environment variable that doesn't
            # show up in os.environ, so we have to get it another way.
            machtype = subprocess.check_output(["bash", "-c", "echo $MACHTYPE"]).rstrip()
            supps_file2 = os.path.join(supps_dir, machtype + ".sup")
            if os.path.isfile(supps_file2):
                valgrind_args.append("--suppressions=" + supps_file2)

            exitcode = None
            timeout = 1800
            try:
                runner = FirefoxRunner(
                    profile=profile,
                    binary=self.get_binary_path(),
                    cmdargs=firefox_args,
                    env=env,
                    process_args=kp_kwargs,
                )
                runner.start(debug_args=valgrind_args)
                exitcode = runner.wait(timeout=timeout)

            finally:
                errs = outputHandler.error_count
                supps = outputHandler.suppression_count
                if errs != supps:
                    status = 1  # turns the TBPL job orange
                    self.log(
                        logging.ERROR,
                        "valgrind-fail-parsing",
                        {"errs": errs, "supps": supps},
                        "TEST-UNEXPECTED-FAIL | valgrind-test | error parsing: {errs} errors seen, but {supps} generated suppressions seen",
                    )

                elif errs == 0:
                    status = 0
                    self.log(logging.INFO, "valgrind-pass", {}, "TEST-PASS | valgrind-test | valgrind found no errors")
                else:
                    status = 1  # turns the TBPL job orange
                    # We've already printed details of the errors.

                if exitcode == None:
                    status = 2  # turns the TBPL job red
                    self.log(
                        logging.ERROR,
                        "valgrind-fail-timeout",
                        {"timeout": timeout},
                        "TEST-UNEXPECTED-FAIL | valgrind-test | Valgrind timed out (reached {timeout} second limit)",
                    )
                elif exitcode != 0:
                    status = 2  # turns the TBPL job red
                    self.log(
                        logging.ERROR,
                        "valgrind-fail-errors",
                        {},
                        "TEST-UNEXPECTED-FAIL | valgrind-test | non-zero exit code from Valgrind",
                    )

                httpd.stop()

            return status
예제 #18
0
    def run(self):
        if self.options.remote_webserver:
            httpd_host = self.options.remote_webserver.split(':')[0]
        else:
            httpd_host = self.httpd.host
        httpd_port = self.httpd.httpd.server_port

        locations = ServerLocations()
        locations.add_host(host=httpd_host,
                           port=httpd_port,
                           options='primary,privileged')

        #TODO: use Preferences.read when prefs_general.js has been updated
        prefpath = self.options.prefs
        prefs = {}
        prefs.update(Preferences.read_prefs(prefpath))
        interpolation = {
            "server": "%s:%d" % (httpd_host, httpd_port),
            "OOP": "false"
        }
        prefs = json.loads(json.dumps(prefs) % interpolation)
        for pref in prefs:
            prefs[pref] = Preferences.cast(prefs[pref])
        prefs[
            "steeplechase.signalling_server"] = self.options.signalling_server
        prefs["steeplechase.signalling_room"] = str(uuid.uuid4())
        if self.options.timeout is None:
            prefs["steeplechase.timeout"] = 30000
        else:
            prefs["steeplechase.timeout"] = self.options.timeout
        prefs["media.navigator.permission.disabled"] = True
        prefs["media.navigator.streams.fake"] = True

        specialpowers_path = self.options.specialpowers
        threads = []
        results = []
        cond = threading.Condition()
        for info in self.remote_info:
            with mozfile.TemporaryDirectory() as profile_path:
                # Create and push profile
                print "Writing profile for %s..." % info['name']
                prefs["steeplechase.is_initiator"] = info['is_initiator']
                profile = FirefoxProfile(profile=profile_path,
                                         preferences=prefs,
                                         addons=[specialpowers_path],
                                         locations=locations)
                print "Pushing profile to %s..." % info['name']
                remote_profile_path = posixpath.join(info['test_root'],
                                                     "profile")
                info['dm'].mkDir(remote_profile_path)
                info['dm'].pushDir(profile_path, remote_profile_path)
                info['remote_profile_path'] = remote_profile_path

            env = {}
            env["MOZ_CRASHREPORTER_NO_REPORT"] = "1"
            env["XPCOM_DEBUG_BREAK"] = "warn"
            env["DISPLAY"] = self.options.remote_xdisplay

            cmd = [
                info['remote_app_path'], "-no-remote", "-profile",
                info['remote_profile_path'],
                'http://%s:%d/index.html' % (httpd_host, httpd_port)
            ]
            print "cmd: %s" % (cmd, )
            t = RunThread(name=info['name'],
                          args=(info['dm'], cmd, env, cond, results))
            threads.append(t)

        for t in threads:
            t.start()

        self.log.info("Waiting for results...")
        pass_count, fail_count = 0, 0
        outputs = {}
        while threads:
            cond.acquire()
            while not results:
                cond.wait()
            thread, result, output = results.pop(0)
            cond.release()
            outputs[thread.name] = output
            passes, failures = result
            #XXX: double-counting tests from both clients. Ok?
            pass_count += passes
            fail_count += failures
            if failures:
                self.log.error("Error in %s" % thread.name)
            threads.remove(thread)
        self.log.info("All clients finished")
        for info in self.remote_info:
            if self.options.log_dest:
                with open(
                        os.path.join(self.options.log_dest,
                                     "%s.log" % info['name']), "wb") as f:
                    f.write(outputs[info['name']])
            if fail_count:
                self.log.info("Log output for %s:", info["name"])
                self.log.info(">>>>>>>")
                for line in outputs[info['name']].splitlines():
                    #TODO: make structured log messages human-readable
                    self.log.info(line)
                self.log.info("<<<<<<<")
        return pass_count, fail_count
    def run(self):
        if self.options.remote_webserver:
            httpd_host = self.options.remote_webserver.split(':')[0]
        else:
            httpd_host = self.httpd.host
        httpd_port = self.httpd.httpd.server_port

        locations = ServerLocations()
        locations.add_host(host=httpd_host,
                           port=httpd_port,
                           options='primary,privileged')

        #TODO: use Preferences.read when prefs_general.js has been updated
        prefpath = self.options.prefs
        prefs = {}
        prefs.update(Preferences.read_prefs(prefpath))
        interpolation = { "server": "%s:%d" % (httpd_host, httpd_port),
                          "OOP": "false"}
        prefs = json.loads(json.dumps(prefs) % interpolation)
        for pref in prefs:
          prefs[pref] = Preferences.cast(prefs[pref])
        prefs["steeplechase.signalling_server"] = self.options.signalling_server
        prefs["steeplechase.signalling_room"] = str(uuid.uuid4())
        prefs["media.navigator.permission.disabled"] = True
        prefs["media.navigator.streams.fake"] = True

        specialpowers_path = self.options.specialpowers
        threads = []
        results = []
        cond = threading.Condition()
        for info in self.remote_info:
            with mozfile.TemporaryDirectory() as profile_path:
                # Create and push profile
                print "Writing profile for %s..." % info['name']
                prefs["steeplechase.is_initiator"] = info['is_initiator']
                profile = FirefoxProfile(profile=profile_path,
                                         preferences=prefs,
                                         addons=[specialpowers_path],
                                         locations=locations)
                print "Pushing profile to %s..." % info['name']
                remote_profile_path = posixpath.join(info['test_root'], "profile")
                info['dm'].mkDir(remote_profile_path)
                info['dm'].pushDir(profile_path, remote_profile_path)
                info['remote_profile_path'] = remote_profile_path

            env = {}
            env["MOZ_CRASHREPORTER_NO_REPORT"] = "1"
            env["XPCOM_DEBUG_BREAK"] = "warn"
            env["DISPLAY"] = self.options.remote_xdisplay

            cmd = [info['remote_app_path'], "-no-remote",
                   "-profile", info['remote_profile_path'],
                   'http://%s:%d/index.html' % (httpd_host, httpd_port)]
            print "cmd: %s" % (cmd, )
            t = RunThread(name=info['name'],
                          args=(info['dm'], cmd, env, cond, results))
            threads.append(t)

        for t in threads:
            t.start()

        self.log.info("Waiting for results...")
        pass_count, fail_count = 0, 0
        outputs = {}
        while threads:
            cond.acquire()
            while not results:
                cond.wait()
            thread, result, output = results.pop(0)
            cond.release()
            outputs[thread.name] = output
            passes, failures = result
            #XXX: double-counting tests from both clients. Ok?
            pass_count += passes
            fail_count += failures
            if failures:
                self.log.error("Error in %s" % thread.name)
            threads.remove(thread)
        self.log.info("All clients finished")
        for info in self.remote_info:
            if self.options.log_dest:
                with open(os.path.join(self.options.log_dest,
                                       "%s.log" % info['name']), "wb") as f:
                    f.write(outputs[info['name']])
            if fail_count:
                self.log.info("Log output for %s:", info["name"])
                self.log.info(">>>>>>>")
                for line in outputs[info['name']].splitlines():
                    #TODO: make structured log messages human-readable
                    self.log.info(line)
                self.log.info("<<<<<<<")
        return pass_count, fail_count
예제 #20
0
    def valgrind_test(self, suppressions):
        import json
        import sys
        import tempfile

        from mozbuild.base import MozbuildObject
        from mozfile import TemporaryDirectory
        from mozhttpd import MozHttpd
        from mozprofile import FirefoxProfile, Preferences
        from mozprofile.permissions import ServerLocations
        from mozrunner import FirefoxRunner
        from mozrunner.utils import findInPath
        from valgrind.output_handler import OutputHandler

        build_dir = os.path.join(self.topsrcdir, 'build')

        # XXX: currently we just use the PGO inputs for Valgrind runs.  This may
        # change in the future.
        httpd = MozHttpd(docroot=os.path.join(build_dir, 'pgo'))
        httpd.start(block=False)

        with TemporaryDirectory() as profilePath:
            #TODO: refactor this into mozprofile
            prefpath = os.path.join(self.topsrcdir, 'testing', 'profiles', 'prefs_general.js')
            prefs = {}
            prefs.update(Preferences.read_prefs(prefpath))
            interpolation = { 'server': '%s:%d' % httpd.httpd.server_address,
                              'OOP': 'false'}
            prefs = json.loads(json.dumps(prefs) % interpolation)
            for pref in prefs:
                prefs[pref] = Preferences.cast(prefs[pref])

            quitter = os.path.join(self.distdir, 'xpi-stage', 'quitter')

            locations = ServerLocations()
            locations.add_host(host='127.0.0.1',
                               port=httpd.httpd.server_port,
                               options='primary')

            profile = FirefoxProfile(profile=profilePath,
                                     preferences=prefs,
                                     addons=[quitter],
                                     locations=locations)

            firefox_args = [httpd.get_url()]

            env = os.environ.copy()
            env['G_SLICE'] = 'always-malloc'
            env['XPCOM_CC_RUN_DURING_SHUTDOWN'] = '1'
            env['MOZ_CRASHREPORTER_NO_REPORT'] = '1'
            env['XPCOM_DEBUG_BREAK'] = 'warn'

            outputHandler = OutputHandler()
            kp_kwargs = {'processOutputLine': [outputHandler]}

            valgrind = 'valgrind'
            if not os.path.exists(valgrind):
                valgrind = findInPath(valgrind)

            valgrind_args = [
                valgrind,
                '--smc-check=all-non-file',
                '--vex-iropt-register-updates=allregs-at-mem-access',
                '--gen-suppressions=all',
                '--num-callers=20',
                '--leak-check=full',
                '--show-possibly-lost=no',
                '--track-origins=yes'
            ]

            for s in suppressions:
                valgrind_args.append('--suppressions=' + s)

            supps_dir = os.path.join(build_dir, 'valgrind')
            supps_file1 = os.path.join(supps_dir, 'cross-architecture.sup')
            valgrind_args.append('--suppressions=' + supps_file1)

            # MACHTYPE is an odd bash-only environment variable that doesn't
            # show up in os.environ, so we have to get it another way.
            machtype = subprocess.check_output(['bash', '-c', 'echo $MACHTYPE']).rstrip()
            supps_file2 = os.path.join(supps_dir, machtype + '.sup')
            if os.path.isfile(supps_file2):
                valgrind_args.append('--suppressions=' + supps_file2)

            exitcode = None
            try:
                runner = FirefoxRunner(profile=profile,
                                       binary=self.get_binary_path(),
                                       cmdargs=firefox_args,
                                       env=env,
                                       kp_kwargs=kp_kwargs)
                runner.start(debug_args=valgrind_args)
                exitcode = runner.wait()

            finally:
                errs = outputHandler.error_count
                supps = outputHandler.suppression_count
                if errs != supps:
                    status = 1  # turns the TBPL job orange
                    print('TEST-UNEXPECTED-FAILURE | valgrind-test | error parsing:', errs, "errors seen, but", supps, "generated suppressions seen")

                elif errs == 0:
                    status = 0
                    print('TEST-PASS | valgrind-test | valgrind found no errors')
                else:
                    status = 1  # turns the TBPL job orange
                    # We've already printed details of the errors.

                if exitcode != 0:
                    status = 2  # turns the TBPL job red
                    print('TEST-UNEXPECTED-FAIL | valgrind-test | non-zero exit code from Valgrind')

                httpd.stop()

            return status
예제 #21
0
    def valgrind_test(self, suppressions):
        import json
        import re
        import sys
        import tempfile

        from mozbuild.base import MozbuildObject
        from mozfile import TemporaryDirectory
        from mozhttpd import MozHttpd
        from mozprofile import FirefoxProfile, Preferences
        from mozprofile.permissions import ServerLocations
        from mozrunner import FirefoxRunner
        from mozrunner.utils import findInPath

        build_dir = os.path.join(self.topsrcdir, "build")

        # XXX: currently we just use the PGO inputs for Valgrind runs.  This may
        # change in the future.
        httpd = MozHttpd(docroot=os.path.join(build_dir, "pgo"))
        httpd.start(block=False)

        with TemporaryDirectory() as profilePath:
            # TODO: refactor this into mozprofile
            prefpath = os.path.join(self.topsrcdir, "testing", "profiles", "prefs_general.js")
            prefs = {}
            prefs.update(Preferences.read_prefs(prefpath))
            interpolation = {"server": "%s:%d" % httpd.httpd.server_address, "OOP": "false"}
            prefs = json.loads(json.dumps(prefs) % interpolation)
            for pref in prefs:
                prefs[pref] = Preferences.cast(prefs[pref])

            quitter = os.path.join(self.distdir, "xpi-stage", "quitter")

            locations = ServerLocations()
            locations.add_host(host="127.0.0.1", port=httpd.httpd.server_port, options="primary")

            profile = FirefoxProfile(profile=profilePath, preferences=prefs, addons=[quitter], locations=locations)

            firefox_args = [httpd.get_url()]

            env = os.environ.copy()
            env["G_SLICE"] = "always-malloc"
            env["XPCOM_CC_RUN_DURING_SHUTDOWN"] = "1"
            env["MOZ_CRASHREPORTER_NO_REPORT"] = "1"
            env["XPCOM_DEBUG_BREAK"] = "warn"

            class OutputHandler(object):
                def __init__(self):
                    self.found_errors = False

                def __call__(self, line):
                    print(line)
                    m = re.match(r".*ERROR SUMMARY: [1-9]\d* errors from \d+ contexts", line)
                    if m:
                        self.found_errors = True

            outputHandler = OutputHandler()
            kp_kwargs = {"processOutputLine": [outputHandler]}

            valgrind = "valgrind"
            if not os.path.exists(valgrind):
                valgrind = findInPath(valgrind)

            valgrind_args = [
                valgrind,
                "--smc-check=all-non-file",
                "--vex-iropt-register-updates=allregs-at-mem-access",
                "--gen-suppressions=all",
                "--num-callers=20",
                "--leak-check=full",
                "--show-possibly-lost=no",
                "--track-origins=yes",
            ]

            for s in suppressions:
                valgrind_args.append("--suppressions=" + s)

            supps_dir = os.path.join(build_dir, "valgrind")
            supps_file1 = os.path.join(supps_dir, "cross-architecture.sup")
            valgrind_args.append("--suppressions=" + supps_file1)

            # MACHTYPE is an odd bash-only environment variable that doesn't
            # show up in os.environ, so we have to get it another way.
            machtype = subprocess.check_output(["bash", "-c", "echo $MACHTYPE"]).rstrip()
            supps_file2 = os.path.join(supps_dir, machtype + ".sup")
            if os.path.isfile(supps_file2):
                valgrind_args.append("--suppressions=" + supps_file2)

            exitcode = None
            try:
                runner = FirefoxRunner(
                    profile=profile, binary=self.get_binary_path(), cmdargs=firefox_args, env=env, kp_kwargs=kp_kwargs
                )
                runner.start(debug_args=valgrind_args)
                exitcode = runner.wait()

            finally:
                if not outputHandler.found_errors:
                    status = 0
                    print("TEST-PASS | valgrind-test | valgrind found no errors")
                else:
                    status = 1  # turns the TBPL job orange
                    print("TEST-UNEXPECTED-FAIL | valgrind-test | valgrind found errors")

                if exitcode != 0:
                    status = 2  # turns the TBPL job red
                    print("TEST-UNEXPECTED-FAIL | valgrind-test | non-zero exit code from Valgrind")

                httpd.stop()

            return status
예제 #22
0
    def valgrind_test(self, suppressions):

        from mozfile import TemporaryDirectory
        from mozhttpd import MozHttpd
        from mozprofile import FirefoxProfile, Preferences
        from mozprofile.permissions import ServerLocations
        from mozrunner import FirefoxRunner
        from mozrunner.utils import findInPath
        from six import string_types
        from valgrind.output_handler import OutputHandler

        build_dir = os.path.join(self.topsrcdir, 'build')

        # XXX: currently we just use the PGO inputs for Valgrind runs.  This may
        # change in the future.
        httpd = MozHttpd(docroot=os.path.join(build_dir, 'pgo'))
        httpd.start(block=False)

        with TemporaryDirectory() as profilePath:
            # TODO: refactor this into mozprofile
            profile_data_dir = os.path.join(
                self.topsrcdir, 'testing', 'profiles')
            with open(os.path.join(profile_data_dir, 'profiles.json'), 'r') as fh:
                base_profiles = json.load(fh)['valgrind']

            prefpaths = [os.path.join(profile_data_dir, profile, 'user.js')
                         for profile in base_profiles]
            prefs = {}
            for path in prefpaths:
                prefs.update(Preferences.read_prefs(path))

            interpolation = {
                'server': '%s:%d' % httpd.httpd.server_address,
            }
            for k, v in prefs.items():
                if isinstance(v, string_types):
                    v = v.format(**interpolation)
                prefs[k] = Preferences.cast(v)

            quitter = os.path.join(
                self.topsrcdir, 'tools', 'quitter', '*****@*****.**')

            locations = ServerLocations()
            locations.add_host(host='127.0.0.1',
                               port=httpd.httpd.server_port,
                               options='primary')

            profile = FirefoxProfile(profile=profilePath,
                                     preferences=prefs,
                                     addons=[quitter],
                                     locations=locations)

            firefox_args = [httpd.get_url()]

            env = os.environ.copy()
            env['G_SLICE'] = 'always-malloc'
            env['MOZ_CC_RUN_DURING_SHUTDOWN'] = '1'
            env['MOZ_CRASHREPORTER_NO_REPORT'] = '1'
            env['XPCOM_DEBUG_BREAK'] = 'warn'

            env.update(self.extra_environment_variables)

            outputHandler = OutputHandler(self.log)
            kp_kwargs = {'processOutputLine': [outputHandler]}

            valgrind = 'valgrind'
            if not os.path.exists(valgrind):
                valgrind = findInPath(valgrind)

            valgrind_args = [
                valgrind,
                '--smc-check=all-non-file',
                '--vex-iropt-register-updates=allregs-at-mem-access',
                '--gen-suppressions=all',
                '--num-callers=36',
                '--leak-check=full',
                '--show-possibly-lost=no',
                '--track-origins=yes',
                '--trace-children=yes',
                '-v',  # Enable verbosity to get the list of used suppressions
                # Avoid excessive delays in the presence of spinlocks.
                # See bug 1309851.
                '--fair-sched=yes',
                # Keep debuginfo after library unmap.  See bug 1382280.
                '--keep-debuginfo=yes',
                # Reduce noise level on rustc and/or LLVM compiled code.
                # See bug 1365915
                '--expensive-definedness-checks=yes',
            ]

            for s in suppressions:
                valgrind_args.append('--suppressions=' + s)

            supps_dir = os.path.join(build_dir, 'valgrind')
            supps_file1 = os.path.join(supps_dir, 'cross-architecture.sup')
            valgrind_args.append('--suppressions=' + supps_file1)

            if mozinfo.os == 'linux':
                machtype = {
                    'x86_64': 'x86_64-pc-linux-gnu',
                    'x86': 'i386-pc-linux-gnu',
                }.get(mozinfo.processor)
                if machtype:
                    supps_file2 = os.path.join(supps_dir, machtype + '.sup')
                    if os.path.isfile(supps_file2):
                        valgrind_args.append('--suppressions=' + supps_file2)

            exitcode = None
            timeout = 1800
            try:
                runner = FirefoxRunner(profile=profile,
                                       binary=self.get_binary_path(),
                                       cmdargs=firefox_args,
                                       env=env,
                                       process_args=kp_kwargs)
                runner.start(debug_args=valgrind_args)
                exitcode = runner.wait(timeout=timeout)

            finally:
                errs = outputHandler.error_count
                supps = outputHandler.suppression_count
                if errs != supps:
                    status = 1  # turns the TBPL job orange
                    self.log(logging.ERROR, 'valgrind-fail-parsing',
                             {'errs': errs, 'supps': supps},
                             'TEST-UNEXPECTED-FAIL | valgrind-test | error parsing: {errs} errors '
                             'seen, but {supps} generated suppressions seen')

                elif errs == 0:
                    status = 0
                    self.log(logging.INFO, 'valgrind-pass', {},
                             'TEST-PASS | valgrind-test | valgrind found no errors')
                else:
                    status = 1  # turns the TBPL job orange
                    # We've already printed details of the errors.

                if exitcode is None:
                    status = 2  # turns the TBPL job red
                    self.log(logging.ERROR, 'valgrind-fail-timeout',
                             {'timeout': timeout},
                             'TEST-UNEXPECTED-FAIL | valgrind-test | Valgrind timed out '
                             '(reached {timeout} second limit)')
                elif exitcode != 0:
                    status = 2  # turns the TBPL job red
                    self.log(logging.ERROR, 'valgrind-fail-errors', {},
                             'TEST-UNEXPECTED-FAIL | valgrind-test | non-zero exit code'
                             'from Valgrind')

                httpd.stop()

            return status
예제 #23
0
    locations.add_host(host='127.0.0.1',
                       port=PORT,
                       options='primary,privileged')

    with TemporaryDirectory() as profilePath:
        # TODO: refactor this into mozprofile
        profile_data_dir = os.path.join(build.topsrcdir, 'testing', 'profiles')
        with open(os.path.join(profile_data_dir, 'profiles.json'), 'r') as fh:
            base_profiles = json.load(fh)['profileserver']

        prefpaths = [os.path.join(profile_data_dir, profile, 'user.js')
                     for profile in base_profiles]

        prefs = {}
        for path in prefpaths:
            prefs.update(Preferences.read_prefs(path))

        interpolation = {"server": "%s:%d" % httpd.httpd.server_address,
                         "OOP": "false"}
        for k, v in prefs.items():
            if isinstance(v, string_types):
                v = v.format(**interpolation)
            prefs[k] = Preferences.cast(v)

        profile = FirefoxProfile(profile=profilePath,
                                 preferences=prefs,
                                 addons=[os.path.join(
                                     build.topsrcdir, 'tools', 'quitter',
                                     '*****@*****.**')],
                                 locations=locations)
예제 #24
0
    def valgrind_test(self, suppressions):
        import json
        import re
        import sys
        import tempfile

        from mozbuild.base import MozbuildObject
        from mozfile import TemporaryDirectory
        from mozhttpd import MozHttpd
        from mozprofile import FirefoxProfile, Preferences
        from mozprofile.permissions import ServerLocations
        from mozrunner import FirefoxRunner
        from mozrunner.utils import findInPath

        build_dir = os.path.join(self.topsrcdir, 'build')

        # XXX: currently we just use the PGO inputs for Valgrind runs.  This may
        # change in the future.
        httpd = MozHttpd(docroot=os.path.join(build_dir, 'pgo'))
        httpd.start(block=False)

        with TemporaryDirectory() as profilePath:
            #TODO: refactor this into mozprofile
            prefpath = os.path.join(self.topsrcdir, 'testing', 'profiles',
                                    'prefs_general.js')
            prefs = {}
            prefs.update(Preferences.read_prefs(prefpath))
            interpolation = {
                'server': '%s:%d' % httpd.httpd.server_address,
                'OOP': 'false'
            }
            prefs = json.loads(json.dumps(prefs) % interpolation)
            for pref in prefs:
                prefs[pref] = Preferences.cast(prefs[pref])

            quitter = os.path.join(self.distdir, 'xpi-stage', 'quitter')

            locations = ServerLocations()
            locations.add_host(host='127.0.0.1',
                               port=httpd.httpd.server_port,
                               options='primary')

            profile = FirefoxProfile(profile=profilePath,
                                     preferences=prefs,
                                     addons=[quitter],
                                     locations=locations)

            firefox_args = [httpd.get_url()]

            env = os.environ.copy()
            env['G_SLICE'] = 'always-malloc'
            env['XPCOM_CC_RUN_DURING_SHUTDOWN'] = '1'
            env['MOZ_CRASHREPORTER_NO_REPORT'] = '1'
            env['XPCOM_DEBUG_BREAK'] = 'warn'

            class OutputHandler(object):
                def __init__(self):
                    self.found_errors = False

                def __call__(self, line):
                    print(line)
                    m = re.match(
                        r'.*ERROR SUMMARY: [1-9]\d* errors from \d+ contexts',
                        line)
                    if m:
                        self.found_errors = True

            outputHandler = OutputHandler()
            kp_kwargs = {'processOutputLine': [outputHandler]}

            valgrind = 'valgrind'
            if not os.path.exists(valgrind):
                valgrind = findInPath(valgrind)

            valgrind_args = [
                valgrind, '--smc-check=all-non-file',
                '--vex-iropt-register-updates=allregs-at-mem-access',
                '--gen-suppressions=all', '--num-callers=20',
                '--leak-check=full', '--show-possibly-lost=no',
                '--track-origins=yes'
            ]

            for s in suppressions:
                valgrind_args.append('--suppressions=' + s)

            supps_dir = os.path.join(build_dir, 'valgrind')
            supps_file1 = os.path.join(supps_dir, 'cross-architecture.sup')
            valgrind_args.append('--suppressions=' + supps_file1)

            # MACHTYPE is an odd bash-only environment variable that doesn't
            # show up in os.environ, so we have to get it another way.
            machtype = subprocess.check_output(
                ['bash', '-c', 'echo $MACHTYPE']).rstrip()
            supps_file2 = os.path.join(supps_dir, machtype + '.sup')
            if os.path.isfile(supps_file2):
                valgrind_args.append('--suppressions=' + supps_file2)

            exitcode = None
            try:
                runner = FirefoxRunner(profile=profile,
                                       binary=self.get_binary_path(),
                                       cmdargs=firefox_args,
                                       env=env,
                                       kp_kwargs=kp_kwargs)
                runner.start(debug_args=valgrind_args)
                exitcode = runner.wait()

            finally:
                if not outputHandler.found_errors:
                    status = 0
                    print(
                        'TEST-PASS | valgrind-test | valgrind found no errors')
                else:
                    status = 1  # turns the TBPL job orange
                    print(
                        'TEST-UNEXPECTED-FAIL | valgrind-test | valgrind found errors'
                    )

                if exitcode != 0:
                    status = 2  # turns the TBPL job red
                    print(
                        'TEST-UNEXPECTED-FAIL | valgrind-test | non-zero exit code from Valgrind'
                    )

                httpd.stop()

            return status
예제 #25
0
    def valgrind_test(self, suppressions):
        import json
        import sys
        import tempfile

        from mozbuild.base import MozbuildObject
        from mozfile import TemporaryDirectory
        from mozhttpd import MozHttpd
        from mozprofile import FirefoxProfile, Preferences
        from mozprofile.permissions import ServerLocations
        from mozrunner import FirefoxRunner
        from mozrunner.utils import findInPath
        from valgrind.output_handler import OutputHandler

        build_dir = os.path.join(self.topsrcdir, 'build')

        # XXX: currently we just use the PGO inputs for Valgrind runs.  This may
        # change in the future.
        httpd = MozHttpd(docroot=os.path.join(build_dir, 'pgo'))
        httpd.start(block=False)

        with TemporaryDirectory() as profilePath:
            #TODO: refactor this into mozprofile
            prefpath = os.path.join(self.topsrcdir, 'testing', 'profiles', 'prefs_general.js')
            prefs = {}
            prefs.update(Preferences.read_prefs(prefpath))
            interpolation = { 'server': '%s:%d' % httpd.httpd.server_address,
                              'OOP': 'false'}
            prefs = json.loads(json.dumps(prefs) % interpolation)
            for pref in prefs:
                prefs[pref] = Preferences.cast(prefs[pref])

            quitter = os.path.join(self.distdir, 'xpi-stage', 'quitter')

            locations = ServerLocations()
            locations.add_host(host='127.0.0.1',
                               port=httpd.httpd.server_port,
                               options='primary')

            profile = FirefoxProfile(profile=profilePath,
                                     preferences=prefs,
                                     addons=[quitter],
                                     locations=locations)

            firefox_args = [httpd.get_url()]

            env = os.environ.copy()
            env['G_SLICE'] = 'always-malloc'
            env['MOZ_CC_RUN_DURING_SHUTDOWN'] = '1'
            env['MOZ_CRASHREPORTER_NO_REPORT'] = '1'
            env['XPCOM_DEBUG_BREAK'] = 'warn'

            env.update(self.extra_environment_variables)

            outputHandler = OutputHandler()
            kp_kwargs = {'processOutputLine': [outputHandler]}

            valgrind = 'valgrind'
            if not os.path.exists(valgrind):
                valgrind = findInPath(valgrind)

            valgrind_args = [
                valgrind,
                '--smc-check=all-non-file',
                '--vex-iropt-register-updates=allregs-at-mem-access',
                '--gen-suppressions=all',
                '--num-callers=36',
                '--leak-check=full',
                '--show-possibly-lost=no',
                '--track-origins=yes',
                '--trace-children=yes',
                # The gstreamer plugin scanner can run as part of executing
                # firefox, but is an external program. In some weird cases,
                # valgrind finds errors while executing __libc_freeres when
                # it runs, but those are not relevant, as it's related to
                # executing third party code. So don't trace
                # gst-plugin-scanner.
                '--trace-children-skip=*/gst-plugin-scanner',
                '-v',  # Enable verbosity to get the list of used suppressions
            ]

            for s in suppressions:
                valgrind_args.append('--suppressions=' + s)

            supps_dir = os.path.join(build_dir, 'valgrind')
            supps_file1 = os.path.join(supps_dir, 'cross-architecture.sup')
            valgrind_args.append('--suppressions=' + supps_file1)

            # MACHTYPE is an odd bash-only environment variable that doesn't
            # show up in os.environ, so we have to get it another way.
            machtype = subprocess.check_output(['bash', '-c', 'echo $MACHTYPE']).rstrip()
            supps_file2 = os.path.join(supps_dir, machtype + '.sup')
            if os.path.isfile(supps_file2):
                valgrind_args.append('--suppressions=' + supps_file2)

            exitcode = None
            timeout = 1100
            try:
                runner = FirefoxRunner(profile=profile,
                                       binary=self.get_binary_path(),
                                       cmdargs=firefox_args,
                                       env=env,
                                       process_args=kp_kwargs)
                runner.start(debug_args=valgrind_args)
                # This timeout is slightly less than the no-output timeout on
                # TBPL, so we'll timeout here first and give an informative
                # message.
                exitcode = runner.wait(timeout=timeout)

            finally:
                errs = outputHandler.error_count
                supps = outputHandler.suppression_count
                if errs != supps:
                    status = 1  # turns the TBPL job orange
                    print('TEST-UNEXPECTED-FAIL | valgrind-test | error parsing: {} errors seen, but {} generated suppressions seen'.format(errs, supps))

                elif errs == 0:
                    status = 0
                    print('TEST-PASS | valgrind-test | valgrind found no errors')
                else:
                    status = 1  # turns the TBPL job orange
                    # We've already printed details of the errors.

                if exitcode == None:
                    status = 2  # turns the TBPL job red
                    print('TEST-UNEXPECTED-FAIL | valgrind-test | Valgrind timed out (reached {} second limit)'.format(timeout))
                elif exitcode != 0:
                    status = 2  # turns the TBPL job red
                    print('TEST-UNEXPECTED-FAIL | valgrind-test | non-zero exit code from Valgrind')

                httpd.stop()

            return status
예제 #26
0
def valgrind_test(command_context, suppressions):
    """
    Run Valgrind tests.
    """

    from mozfile import TemporaryDirectory
    from mozhttpd import MozHttpd
    from mozprofile import FirefoxProfile, Preferences
    from mozprofile.permissions import ServerLocations
    from mozrunner import FirefoxRunner
    from mozrunner.utils import findInPath
    from six import string_types
    from valgrind.output_handler import OutputHandler

    build_dir = os.path.join(command_context.topsrcdir, "build")

    # XXX: currently we just use the PGO inputs for Valgrind runs.  This may
    # change in the future.
    httpd = MozHttpd(docroot=os.path.join(build_dir, "pgo"))
    httpd.start(block=False)

    with TemporaryDirectory() as profilePath:
        # TODO: refactor this into mozprofile
        profile_data_dir = os.path.join(command_context.topsrcdir, "testing",
                                        "profiles")
        with open(os.path.join(profile_data_dir, "profiles.json"), "r") as fh:
            base_profiles = json.load(fh)["valgrind"]

        prefpaths = [
            os.path.join(profile_data_dir, profile, "user.js")
            for profile in base_profiles
        ]
        prefs = {}
        for path in prefpaths:
            prefs.update(Preferences.read_prefs(path))

        interpolation = {
            "server": "%s:%d" % httpd.httpd.server_address,
        }
        for k, v in prefs.items():
            if isinstance(v, string_types):
                v = v.format(**interpolation)
            prefs[k] = Preferences.cast(v)

        quitter = os.path.join(command_context.topsrcdir, "tools", "quitter",
                               "*****@*****.**")

        locations = ServerLocations()
        locations.add_host(host="127.0.0.1",
                           port=httpd.httpd.server_port,
                           options="primary")

        profile = FirefoxProfile(
            profile=profilePath,
            preferences=prefs,
            addons=[quitter],
            locations=locations,
        )

        firefox_args = [httpd.get_url()]

        env = os.environ.copy()
        env["G_SLICE"] = "always-malloc"
        env["MOZ_CC_RUN_DURING_SHUTDOWN"] = "1"
        env["MOZ_CRASHREPORTER_NO_REPORT"] = "1"
        env["MOZ_DISABLE_NONLOCAL_CONNECTIONS"] = "1"
        env["XPCOM_DEBUG_BREAK"] = "warn"

        outputHandler = OutputHandler(command_context.log)
        kp_kwargs = {
            "processOutputLine": [outputHandler],
            "universal_newlines": True,
        }

        valgrind = "valgrind"
        if not os.path.exists(valgrind):
            valgrind = findInPath(valgrind)

        valgrind_args = [
            valgrind,
            "--sym-offsets=yes",
            "--smc-check=all-non-file",
            "--vex-iropt-register-updates=allregs-at-mem-access",
            "--gen-suppressions=all",
            "--num-callers=36",
            "--leak-check=full",
            "--show-possibly-lost=no",
            "--track-origins=yes",
            "--trace-children=yes",
            "-v",  # Enable verbosity to get the list of used suppressions
            # Avoid excessive delays in the presence of spinlocks.
            # See bug 1309851.
            "--fair-sched=yes",
            # Keep debuginfo after library unmap.  See bug 1382280.
            "--keep-debuginfo=yes",
            # Reduce noise level on rustc and/or LLVM compiled code.
            # See bug 1365915
            "--expensive-definedness-checks=yes",
            # Compensate for the compiler inlining `new` but not `delete`
            # or vice versa.
            "--show-mismatched-frees=no",
        ]

        for s in suppressions:
            valgrind_args.append("--suppressions=" + s)

        supps_dir = os.path.join(build_dir, "valgrind")
        supps_file1 = os.path.join(supps_dir, "cross-architecture.sup")
        valgrind_args.append("--suppressions=" + supps_file1)

        if mozinfo.os == "linux":
            machtype = {
                "x86_64": "x86_64-pc-linux-gnu",
                "x86": "i386-pc-linux-gnu",
            }.get(mozinfo.processor)
            if machtype:
                supps_file2 = os.path.join(supps_dir, machtype + ".sup")
                if os.path.isfile(supps_file2):
                    valgrind_args.append("--suppressions=" + supps_file2)

        exitcode = None
        timeout = 1800
        binary_not_found_exception = None
        try:
            runner = FirefoxRunner(
                profile=profile,
                binary=command_context.get_binary_path(),
                cmdargs=firefox_args,
                env=env,
                process_args=kp_kwargs,
            )
            runner.start(debug_args=valgrind_args)
            exitcode = runner.wait(timeout=timeout)
        except BinaryNotFoundException as e:
            binary_not_found_exception = e
        finally:
            errs = outputHandler.error_count
            supps = outputHandler.suppression_count
            if errs != supps:
                status = 1  # turns the TBPL job orange
                command_context.log(
                    logging.ERROR,
                    "valgrind-fail-parsing",
                    {
                        "errs": errs,
                        "supps": supps
                    },
                    "TEST-UNEXPECTED-FAIL | valgrind-test | error parsing: {errs} errors "
                    "seen, but {supps} generated suppressions seen",
                )

            elif errs == 0:
                status = 0
                command_context.log(
                    logging.INFO,
                    "valgrind-pass",
                    {},
                    "TEST-PASS | valgrind-test | valgrind found no errors",
                )
            else:
                status = 1  # turns the TBPL job orange
                # We've already printed details of the errors.

            if binary_not_found_exception:
                status = 2  # turns the TBPL job red
                command_context.log(
                    logging.ERROR,
                    "valgrind-fail-errors",
                    {"error": str(binary_not_found_exception)},
                    "TEST-UNEXPECTED-FAIL | valgrind-test | {error}",
                )
                command_context.log(
                    logging.INFO,
                    "valgrind-fail-errors",
                    {"help": binary_not_found_exception.help()},
                    "{help}",
                )
            elif exitcode is None:
                status = 2  # turns the TBPL job red
                command_context.log(
                    logging.ERROR,
                    "valgrind-fail-timeout",
                    {"timeout": timeout},
                    "TEST-UNEXPECTED-FAIL | valgrind-test | Valgrind timed out "
                    "(reached {timeout} second limit)",
                )
            elif exitcode != 0:
                status = 2  # turns the TBPL job red
                command_context.log(
                    logging.ERROR,
                    "valgrind-fail-errors",
                    {"exitcode": exitcode},
                    "TEST-UNEXPECTED-FAIL | valgrind-test | non-zero exit code "
                    "from Valgrind: {exitcode}",
                )

            httpd.stop()

        return status