build/valgrind/mach_commands.py

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/build/valgrind/mach_commands.py	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,158 @@
     1.4 +# This Source Code Form is subject to the terms of the Mozilla Public
     1.5 +# License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 +# file, You can obtain one at http://mozilla.org/MPL/2.0/.
     1.7 +
     1.8 +from __future__ import print_function, unicode_literals
     1.9 +
    1.10 +import os
    1.11 +import re
    1.12 +import subprocess
    1.13 +
    1.14 +from mach.decorators import (
    1.15 +    Command,
    1.16 +    CommandArgument,
    1.17 +    CommandProvider,
    1.18 +)
    1.19 +from mozbuild.base import (
    1.20 +    MachCommandBase,
    1.21 +    MachCommandConditions as conditions,
    1.22 +)
    1.23 +
    1.24 +
    1.25 +def is_valgrind_build(cls):
    1.26 +    '''Must be a build with --enable-valgrind and --disable-jemalloc.'''
    1.27 +    defines = cls.config_environment.defines
    1.28 +    return 'MOZ_VALGRIND' in defines and 'MOZ_MEMORY' not in defines
    1.29 +
    1.30 +
    1.31 +@CommandProvider
    1.32 +class MachCommands(MachCommandBase):
    1.33 +    '''
    1.34 +    Run Valgrind tests.
    1.35 +    '''
    1.36 +    def __init__(self, context):
    1.37 +        MachCommandBase.__init__(self, context)
    1.38 +
    1.39 +    @Command('valgrind-test', category='testing',
    1.40 +        conditions=[conditions.is_firefox, is_valgrind_build],
    1.41 +        description='Run the Valgrind test job.')
    1.42 +    @CommandArgument('--suppressions', default=[], action='append',
    1.43 +        metavar='FILENAME',
    1.44 +        help='Specify a suppression file for Valgrind to use. Use '
    1.45 +            '--suppression multiple times to specify multiple suppression '
    1.46 +            'files.')
    1.47 +    def valgrind_test(self, suppressions):
    1.48 +        import json
    1.49 +        import sys
    1.50 +        import tempfile
    1.51 +
    1.52 +        from mozbuild.base import MozbuildObject
    1.53 +        from mozfile import TemporaryDirectory
    1.54 +        from mozhttpd import MozHttpd
    1.55 +        from mozprofile import FirefoxProfile, Preferences
    1.56 +        from mozprofile.permissions import ServerLocations
    1.57 +        from mozrunner import FirefoxRunner
    1.58 +        from mozrunner.utils import findInPath
    1.59 +        from valgrind.output_handler import OutputHandler
    1.60 +
    1.61 +        build_dir = os.path.join(self.topsrcdir, 'build')
    1.62 +
    1.63 +        # XXX: currently we just use the PGO inputs for Valgrind runs.  This may
    1.64 +        # change in the future.
    1.65 +        httpd = MozHttpd(docroot=os.path.join(build_dir, 'pgo'))
    1.66 +        httpd.start(block=False)
    1.67 +
    1.68 +        with TemporaryDirectory() as profilePath:
    1.69 +            #TODO: refactor this into mozprofile
    1.70 +            prefpath = os.path.join(self.topsrcdir, 'testing', 'profiles', 'prefs_general.js')
    1.71 +            prefs = {}
    1.72 +            prefs.update(Preferences.read_prefs(prefpath))
    1.73 +            interpolation = { 'server': '%s:%d' % httpd.httpd.server_address,
    1.74 +                              'OOP': 'false'}
    1.75 +            prefs = json.loads(json.dumps(prefs) % interpolation)
    1.76 +            for pref in prefs:
    1.77 +                prefs[pref] = Preferences.cast(prefs[pref])
    1.78 +
    1.79 +            quitter = os.path.join(self.distdir, 'xpi-stage', 'quitter')
    1.80 +
    1.81 +            locations = ServerLocations()
    1.82 +            locations.add_host(host='127.0.0.1',
    1.83 +                               port=httpd.httpd.server_port,
    1.84 +                               options='primary')
    1.85 +
    1.86 +            profile = FirefoxProfile(profile=profilePath,
    1.87 +                                     preferences=prefs,
    1.88 +                                     addons=[quitter],
    1.89 +                                     locations=locations)
    1.90 +
    1.91 +            firefox_args = [httpd.get_url()]
    1.92 +
    1.93 +            env = os.environ.copy()
    1.94 +            env['G_SLICE'] = 'always-malloc'
    1.95 +            env['MOZ_CC_RUN_DURING_SHUTDOWN'] = '1'
    1.96 +            env['MOZ_CRASHREPORTER_NO_REPORT'] = '1'
    1.97 +            env['XPCOM_DEBUG_BREAK'] = 'warn'
    1.98 +
    1.99 +            outputHandler = OutputHandler()
   1.100 +            kp_kwargs = {'processOutputLine': [outputHandler]}
   1.101 +
   1.102 +            valgrind = 'valgrind'
   1.103 +            if not os.path.exists(valgrind):
   1.104 +                valgrind = findInPath(valgrind)
   1.105 +
   1.106 +            valgrind_args = [
   1.107 +                valgrind,
   1.108 +                '--smc-check=all-non-file',
   1.109 +                '--vex-iropt-register-updates=allregs-at-mem-access',
   1.110 +                '--gen-suppressions=all',
   1.111 +                '--num-callers=36',
   1.112 +                '--leak-check=full',
   1.113 +                '--show-possibly-lost=no',
   1.114 +                '--track-origins=yes'
   1.115 +            ]
   1.116 +
   1.117 +            for s in suppressions:
   1.118 +                valgrind_args.append('--suppressions=' + s)
   1.119 +
   1.120 +            supps_dir = os.path.join(build_dir, 'valgrind')
   1.121 +            supps_file1 = os.path.join(supps_dir, 'cross-architecture.sup')
   1.122 +            valgrind_args.append('--suppressions=' + supps_file1)
   1.123 +
   1.124 +            # MACHTYPE is an odd bash-only environment variable that doesn't
   1.125 +            # show up in os.environ, so we have to get it another way.
   1.126 +            machtype = subprocess.check_output(['bash', '-c', 'echo $MACHTYPE']).rstrip()
   1.127 +            supps_file2 = os.path.join(supps_dir, machtype + '.sup')
   1.128 +            if os.path.isfile(supps_file2):
   1.129 +                valgrind_args.append('--suppressions=' + supps_file2)
   1.130 +
   1.131 +            exitcode = None
   1.132 +            try:
   1.133 +                runner = FirefoxRunner(profile=profile,
   1.134 +                                       binary=self.get_binary_path(),
   1.135 +                                       cmdargs=firefox_args,
   1.136 +                                       env=env,
   1.137 +                                       kp_kwargs=kp_kwargs)
   1.138 +                runner.start(debug_args=valgrind_args)
   1.139 +                exitcode = runner.wait()
   1.140 +
   1.141 +            finally:
   1.142 +                errs = outputHandler.error_count
   1.143 +                supps = outputHandler.suppression_count
   1.144 +                if errs != supps:
   1.145 +                    status = 1  # turns the TBPL job orange
   1.146 +                    print('TEST-UNEXPECTED-FAILURE | valgrind-test | error parsing:', errs, "errors seen, but", supps, "generated suppressions seen")
   1.147 +
   1.148 +                elif errs == 0:
   1.149 +                    status = 0
   1.150 +                    print('TEST-PASS | valgrind-test | valgrind found no errors')
   1.151 +                else:
   1.152 +                    status = 1  # turns the TBPL job orange
   1.153 +                    # We've already printed details of the errors.
   1.154 +
   1.155 +                if exitcode != 0:
   1.156 +                    status = 2  # turns the TBPL job red
   1.157 +                    print('TEST-UNEXPECTED-FAIL | valgrind-test | non-zero exit code from Valgrind')
   1.158 +
   1.159 +                httpd.stop()
   1.160 +
   1.161 +            return status

mercurial