| """Run a unittest with a sanitizer. |
| |
| The key feature of this step is that this step will only be marked as failed |
| if the sanitizer detects an issue. If a test fails for another reason, then |
| this step is not considered to have failed. |
| |
| Available sanitizers are: asan, msan, tsan, and ubsan. |
| """ |
| |
| from __future__ import absolute_import |
| import os |
| |
| from helpers import sanitizer_utils |
| from slave.step import shell_step |
| |
| |
| class SanitizerUnitTestShellStep(shell_step.ShellStep): |
| """Unit test step running with a sanitizer .""" |
| |
| def __init__(self, sanitizer=None, **kwargs): |
| """Constructor.""" |
| assert sanitizer in ['asan', 'msan', 'tsan', 'ubsan'] |
| properties_handler = sanitizer_utils.get_sanitizer_to_comments_for( |
| sanitizer) |
| shell_step.ShellStep.__init__( |
| self, properties_handler=properties_handler, **kwargs) |
| self._sanitizer = sanitizer |
| self._unittest_prefix_filter = 'cast_' |
| if 'unittest_prefix_filter' in kwargs: |
| self._unittest_prefix_filter = kwargs['unittest_prefix_filter'] |
| |
| |
| symbolizer = os.path.relpath( |
| os.path.join( |
| self.get_project_path('chromium/src'), |
| 'third_party/llvm-build/Release+Asserts/bin/llvm-symbolizer'), |
| self.directory) |
| suppression_dir = os.path.relpath( |
| os.path.join( |
| self.get_project_path('chromecast/internal'), 'build/sanitizers'), |
| self.directory) |
| asan_options = ('detect_leaks=1 ' |
| 'detect_odr_violation=0 ' |
| 'symbolize=1 ' |
| 'external_symbolizer_path={}'.format(symbolizer)) |
| lsan_options = ('suppressions={}/lsan_suppressions'.format(suppression_dir)) |
| tsan_options = ('external_symbolizer_path={} ' |
| 'suppressions={}/tsan_suppressions').format(symbolizer, |
| suppression_dir) |
| ubsan_options = ('external_symbolizer_path={}'.format(symbolizer)) |
| |
| self._sanitizer_env = { |
| 'ASAN_OPTIONS': asan_options, |
| 'LSAN_OPTIONS': lsan_options, |
| 'MSAN_SYMBOLIZER_PATH': symbolizer, |
| 'TSAN_OPTIONS': tsan_options, |
| 'UBSAN_OPTIONS': ubsan_options, |
| } |
| |
| @property |
| def validator(self): |
| # Non-cast unittests (i.e. upstream Chromium tests) are not MSAN clean, so |
| # do not block on them until upstream MSAN bots are stabilized. |
| # (crbug/658452) |
| if not self._name.startswith(self._unittest_prefix_filter): |
| return lambda *args: True |
| return sanitizer_utils.get_validator_for(self._sanitizer) |
| |
| def get_commands(self): |
| """Returns which commands to be run. Can be overridden by a subclass. |
| |
| Returns: |
| List of ShellCommand objects to execute. |
| """ |
| assert isinstance(self._command, list) |
| return [self.ShellCommand( |
| self._command, |
| validator=self.validator, |
| cwd=self.directory, |
| env=self._sanitizer_env)] |