Bug 1545778 - Consume all translation units when doing coverity integration. r=bastien
authorAndi-Bogdan Postelnicu <bpostelnicu@mozilla.com>
Wed, 24 Apr 2019 15:51:32 +0000
changeset 529504 1e9952764421d6831c1d26b1bf5529fc5b1b2755
parent 529503 b4763accfb725f3f1f0d770ca84b8358030a318d
child 529505 46ecb4e343dbf2da93660540206e9aecb8a91126
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbastien
bugs1545778
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1545778 - Consume all translation units when doing coverity integration. r=bastien Differential Revision: https://phabricator.services.mozilla.com/D28468
python/mozbuild/mozbuild/mach_commands.py
--- a/python/mozbuild/mozbuild/mach_commands.py
+++ b/python/mozbuild/mozbuild/mach_commands.py
@@ -1879,41 +1879,45 @@ class StaticAnalysis(MachCommandBase):
         if self.substs['MOZ_BUILD_APP'] == 'mobile/android':
             rc = self.check_java(source, jobs, strip, verbose, skip_export=True)
         return rc
 
     @StaticAnalysisSubCommand('static-analysis', 'check-coverity',
                               'Run coverity static-analysis tool on the given files. '
                               'Can only be run by automation! '
                               'It\'s result is stored as an json file on the artifacts server.')
-    @CommandArgument('source', nargs='*', default=['.*'],
+    @CommandArgument('source', nargs='*', default=[],
                      help='Source files to be analyzed by Coverity Static Analysis Tool. '
                           'This is ran only in automation.')
     @CommandArgument('--output', '-o', default=None,
                      help='Write coverity output translated to json output in a file')
     @CommandArgument('--coverity_output_path', '-co', default=None,
                      help='Path where to write coverity results as cov-results.json. '
                      'If no path is specified the default path from the coverity working directory, '
                      '~./mozbuild/coverity is used.')
     @CommandArgument('--outgoing', default=False, action='store_true',
                      help='Run coverity on outgoing files from mercurial or git repository')
-    def check_coverity(self, source=None, output=None, coverity_output_path=None, outgoing=False, verbose=False):
+    def check_coverity(self, source=[], output=None, coverity_output_path=None, outgoing=False, verbose=False):
         self._set_log_level(verbose)
         self.log_manager.enable_all_structured_loggers()
 
         if 'MOZ_AUTOMATION' not in os.environ:
             self.log(logging.INFO, 'static-analysis', {}, 'Coverity based static-analysis cannot be ran outside automation.')
             return
 
         # Use outgoing files instead of source files
         if outgoing:
             repo = get_repository_object(self.topsrcdir)
             files = repo.get_outgoing_files()
             source = map(os.path.abspath, files)
 
+        if len(source) == 0:
+            self.log(logging.ERROR, 'static-analysis', {}, 'There are no files that coverity can use to scan.')
+            return 0
+
         rc = self._build_compile_db(verbose=verbose)
         rc = rc or self._build_export(jobs=2, verbose=verbose)
 
         if rc != 0:
             return rc
 
         commands_list = self.get_files_with_commands(source)
         if len(commands_list) == 0:
@@ -1959,25 +1963,24 @@ class StaticAnalysis(MachCommandBase):
                 return cmd
 
         if coverity_output_path is None:
             cov_result = mozpath.join(self.cov_state_path, 'cov-results.json')
         else:
             cov_result = mozpath.join(coverity_output_path, 'cov-results.json')
 
         # Once the capture is performed we need to do the actual Coverity Desktop analysis
-        cmd = [self.cov_run_desktop, '--json-output-v6', cov_result, '--strip-path', self.topsrcdir]
-        cmd += [element['file'] for element in commands_list]
+        cmd = [self.cov_run_desktop, '--json-output-v6', cov_result, '--analyze-captured-source']
         self.log(logging.INFO, 'static-analysis', {}, 'Running Coverity Analysis for {}'.format(cmd))
         rc = self.run_process(cmd, cwd=self.cov_state_path, pass_thru=True)
         if rc != 0:
             self.log(logging.ERROR, 'static-analysis', {}, 'Coverity Analysis failed!')
 
         if output is not None:
-            self.dump_cov_artifact(cov_result, output)
+            self.dump_cov_artifact(cov_result, source, output)
 
     def get_reliability_index_for_cov_checker(self, checker_name):
         if self._cov_config is None:
             self.log(logging.INFO, 'static-analysis', {}, 'Coverity config file not found, '
                 'using default-value \'reliablity\' = medium. for checker {}'.format(checker_name))
             return 'medium'
 
         checkers = self._cov_config['coverity_checkers']
@@ -1992,17 +1995,17 @@ class StaticAnalysis(MachCommandBase):
             self.log(logging.INFO, 'static-analysis', {},
                 'Coverity checker {} doesn\'t have a reliability index set, '
                 'field \'reliability is missing\', please cosinder adding it. '
                 'For the moment we shall use the default \'reliablity\' = medium.'.format(checker_name))
             return 'medium'
 
         return checkers[checker_name]['reliability']
 
-    def dump_cov_artifact(self, cov_results, output):
+    def dump_cov_artifact(self, cov_results, source, output):
         # Parse Coverity json into structured issues
         with open(cov_results) as f:
             result = json.load(f)
 
             # Parse the issues to a standard json format
             issues_dict = {'files': {}}
 
             files_list = issues_dict['files']
@@ -2028,17 +2031,22 @@ class StaticAnalysis(MachCommandBase):
                     dict_issue['extra']['stack'].append({'file_path': event['strippedFilePathname'],
                                                          'line_number': event['lineNumber'],
                                                          'path_type': event['eventTag'],
                                                          'description': event['eventDescription']})
 
                 return dict_issue
 
             for issue in result['issues']:
-                path = issue['strippedMainEventFilePathname'].strip('/')
+                path = self.cov_is_file_in_source(issue['strippedMainEventFilePathname'], source)
+                if path is None:
+                    # Since we skip a result we should log it
+                    self.log(logging.INFO, 'static-analysis', {}, 'Skipping CID: {0} from file: {1} since it\'s not related with the current patch.'.format(
+                        issue['stateOnServer']['cid'], issue['strippedMainEventFilePathname']))
+                    continue
                 if path in files_list:
                     files_list[path]['warnings'].append(build_element(issue))
                 else:
                     files_list[path] = {'warnings': [build_element(issue)]}
 
             with open(output, 'w') as f:
                 json.dump(issues_dict, f)
 
@@ -2144,16 +2152,27 @@ class StaticAnalysis(MachCommandBase):
         self.cov_idir_path = mozpath.join(self.cov_work_path, self.cov_package_ver, 'idir')
 
         if not os.path.exists(self.cov_path):
             self.log(logging.ERROR, 'static-analysis', {}, 'Missing Coverity in {}'.format(self.cov_path))
             return 1
 
         return 0
 
+    def cov_is_file_in_source(self, abs_path, source):
+        # We have as an input an absolute path for whom we verify if it's a symlink,
+        # if so, we follow that symlink and we match it with elements from source.
+        # If the match is done we return abs_path, otherwise None
+        assert isinstance(source, list)
+        if os.path.islink(abs_path):
+            abs_path = os.path.realpath(abs_path)
+        if abs_path in source:
+            return abs_path
+        return None
+
     def get_files_with_commands(self, source):
         '''
         Returns an array of dictionaries having file_path with build command
         '''
 
         compile_db = json.load(open(self._compile_db, 'r'))
 
         commands_list = []