build/valgrind/mach_commands.py

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 # This Source Code Form is subject to the terms of the Mozilla Public
     2 # License, v. 2.0. If a copy of the MPL was not distributed with this
     3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
     5 from __future__ import print_function, unicode_literals
     7 import os
     8 import re
     9 import subprocess
    11 from mach.decorators import (
    12     Command,
    13     CommandArgument,
    14     CommandProvider,
    15 )
    16 from mozbuild.base import (
    17     MachCommandBase,
    18     MachCommandConditions as conditions,
    19 )
    22 def is_valgrind_build(cls):
    23     '''Must be a build with --enable-valgrind and --disable-jemalloc.'''
    24     defines = cls.config_environment.defines
    25     return 'MOZ_VALGRIND' in defines and 'MOZ_MEMORY' not in defines
    28 @CommandProvider
    29 class MachCommands(MachCommandBase):
    30     '''
    31     Run Valgrind tests.
    32     '''
    33     def __init__(self, context):
    34         MachCommandBase.__init__(self, context)
    36     @Command('valgrind-test', category='testing',
    37         conditions=[conditions.is_firefox, is_valgrind_build],
    38         description='Run the Valgrind test job.')
    39     @CommandArgument('--suppressions', default=[], action='append',
    40         metavar='FILENAME',
    41         help='Specify a suppression file for Valgrind to use. Use '
    42             '--suppression multiple times to specify multiple suppression '
    43             'files.')
    44     def valgrind_test(self, suppressions):
    45         import json
    46         import sys
    47         import tempfile
    49         from mozbuild.base import MozbuildObject
    50         from mozfile import TemporaryDirectory
    51         from mozhttpd import MozHttpd
    52         from mozprofile import FirefoxProfile, Preferences
    53         from mozprofile.permissions import ServerLocations
    54         from mozrunner import FirefoxRunner
    55         from mozrunner.utils import findInPath
    56         from valgrind.output_handler import OutputHandler
    58         build_dir = os.path.join(self.topsrcdir, 'build')
    60         # XXX: currently we just use the PGO inputs for Valgrind runs.  This may
    61         # change in the future.
    62         httpd = MozHttpd(docroot=os.path.join(build_dir, 'pgo'))
    63         httpd.start(block=False)
    65         with TemporaryDirectory() as profilePath:
    66             #TODO: refactor this into mozprofile
    67             prefpath = os.path.join(self.topsrcdir, 'testing', 'profiles', 'prefs_general.js')
    68             prefs = {}
    69             prefs.update(Preferences.read_prefs(prefpath))
    70             interpolation = { 'server': '%s:%d' % httpd.httpd.server_address,
    71                               'OOP': 'false'}
    72             prefs = json.loads(json.dumps(prefs) % interpolation)
    73             for pref in prefs:
    74                 prefs[pref] = Preferences.cast(prefs[pref])
    76             quitter = os.path.join(self.distdir, 'xpi-stage', 'quitter')
    78             locations = ServerLocations()
    79             locations.add_host(host='127.0.0.1',
    80                                port=httpd.httpd.server_port,
    81                                options='primary')
    83             profile = FirefoxProfile(profile=profilePath,
    84                                      preferences=prefs,
    85                                      addons=[quitter],
    86                                      locations=locations)
    88             firefox_args = [httpd.get_url()]
    90             env = os.environ.copy()
    91             env['G_SLICE'] = 'always-malloc'
    92             env['MOZ_CC_RUN_DURING_SHUTDOWN'] = '1'
    93             env['MOZ_CRASHREPORTER_NO_REPORT'] = '1'
    94             env['XPCOM_DEBUG_BREAK'] = 'warn'
    96             outputHandler = OutputHandler()
    97             kp_kwargs = {'processOutputLine': [outputHandler]}
    99             valgrind = 'valgrind'
   100             if not os.path.exists(valgrind):
   101                 valgrind = findInPath(valgrind)
   103             valgrind_args = [
   104                 valgrind,
   105                 '--smc-check=all-non-file',
   106                 '--vex-iropt-register-updates=allregs-at-mem-access',
   107                 '--gen-suppressions=all',
   108                 '--num-callers=36',
   109                 '--leak-check=full',
   110                 '--show-possibly-lost=no',
   111                 '--track-origins=yes'
   112             ]
   114             for s in suppressions:
   115                 valgrind_args.append('--suppressions=' + s)
   117             supps_dir = os.path.join(build_dir, 'valgrind')
   118             supps_file1 = os.path.join(supps_dir, 'cross-architecture.sup')
   119             valgrind_args.append('--suppressions=' + supps_file1)
   121             # MACHTYPE is an odd bash-only environment variable that doesn't
   122             # show up in os.environ, so we have to get it another way.
   123             machtype = subprocess.check_output(['bash', '-c', 'echo $MACHTYPE']).rstrip()
   124             supps_file2 = os.path.join(supps_dir, machtype + '.sup')
   125             if os.path.isfile(supps_file2):
   126                 valgrind_args.append('--suppressions=' + supps_file2)
   128             exitcode = None
   129             try:
   130                 runner = FirefoxRunner(profile=profile,
   131                                        binary=self.get_binary_path(),
   132                                        cmdargs=firefox_args,
   133                                        env=env,
   134                                        kp_kwargs=kp_kwargs)
   135                 runner.start(debug_args=valgrind_args)
   136                 exitcode = runner.wait()
   138             finally:
   139                 errs = outputHandler.error_count
   140                 supps = outputHandler.suppression_count
   141                 if errs != supps:
   142                     status = 1  # turns the TBPL job orange
   143                     print('TEST-UNEXPECTED-FAILURE | valgrind-test | error parsing:', errs, "errors seen, but", supps, "generated suppressions seen")
   145                 elif errs == 0:
   146                     status = 0
   147                     print('TEST-PASS | valgrind-test | valgrind found no errors')
   148                 else:
   149                     status = 1  # turns the TBPL job orange
   150                     # We've already printed details of the errors.
   152                 if exitcode != 0:
   153                     status = 2  # turns the TBPL job red
   154                     print('TEST-UNEXPECTED-FAIL | valgrind-test | non-zero exit code from Valgrind')
   156                 httpd.stop()
   158             return status

mercurial