diff --git a/tools/coverity/OWNERS b/tools/coverity/OWNERS new file mode 100644 index 0000000000..b44992b96b --- /dev/null +++ b/tools/coverity/OWNERS @@ -0,0 +1,3 @@ +kjellander@webrtc.org +phoglund@webrtc.org + diff --git a/tools/coverity/coverity.py b/tools/coverity/coverity.py new file mode 100755 index 0000000000..84b95bc824 --- /dev/null +++ b/tools/coverity/coverity.py @@ -0,0 +1,324 @@ +#!/usr/bin/env python +# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +""" +Runs Coverity Static Analysis on a build of WebRTC. + +This script is a modified copy of Chromium's tools/coverity/coverity.py +Changes made: + * Replaced deprecated switches for cov-commit-defects command: + * Using --host instead of --remote + * Using --stream instead of --product + * Removed --cxx (now default enabled) + * Changed cleaning of output path, since WebRTC's out dir is located directly + in trunk/ + * Updated some default constants. + +The script runs on all WebRTC supported platforms. + +On Windows, this script should be run in a Visual Studio Command Prompt, so +that the INCLUDE, LIB, and PATH environment variables are set properly for +Visual Studio. + +Usage examples: + coverity.py + coverity.py --dry-run + coverity.py --target=debug + %comspec% /c ""C:\Program Files\Microsoft Visual Studio 8\VC\vcvarsall.bat" + x86 && C:\Python24\python.exe C:\coverity.py" + +For a full list of options, pass the '--help' switch. + +See http://support.microsoft.com/kb/308569 for running this script as a +Scheduled Task on Windows XP. +""" + +import optparse +import os +import os.path +import shutil +import subprocess +import sys +import time + +# These constants provide default values, but are exposed as command-line +# flags. See the --help for more info. Note that for historical reasons +# (the script started out as Windows-only and has legacy usages which pre-date +# these switches), the constants are all tuned for Windows. +# Usage of this script on Linux pretty much requires explicit +# --source-dir, --coverity-bin-dir, --coverity-intermediate-dir, and +# --coverity-target command line flags. + +WEBRTC_SOURCE_DIR = 'C:\\webrtc.latest' + +# Relative to WEBRTC_SOURCE_DIR. Only applies to Windows platform. +WEBRTC_SOLUTION_FILE = 'webrtc.sln' + +# Relative to WEBRTC_SOURCE_DIR. Only applies to Windows platform. +WEBRTC_SOLUTION_DIR = 'build' + +COVERITY_BIN_DIR = 'C:\\coverity-integrity-center\\static-analysis\\bin' + +COVERITY_INTERMEDIATE_DIR = 'C:\\coverity\\cvbuild\\cr_int' + +COVERITY_ANALYZE_OPTIONS = ('--security --concurrency ' + '--enable ATOMICITY ' + '--enable MISSING_LOCK ' + '--enable DELETE_VOID ' + '--checker-option PASS_BY_VALUE:size_threshold:16 ' + '--checker-option ' + 'USE_AFTER_FREE:allow_simple_use:false ' + '--enable-constraint-fpp ' + '--enable-callgraph-metrics') + +# Might need to be changed to FQDN +COVERITY_REMOTE = 'localhost' + +COVERITY_PORT = '8080' + +COVERITY_STREAM = 'trunk' + +COVERITY_TARGET = 'Windows' + +COVERITY_USER = 'coverityanalyzer' +# looking for a PASSWORD constant? Look at --coverity-password-file instead. + +# Relative to WEBRTC_SOURCE_DIR. Contains the pid of this script. +LOCK_FILE = 'coverity.lock' + + +def _ReadPassword(pwfilename): + """Reads the coverity password in from a file where it was stashed""" + pwfile = open(pwfilename, 'r') + password = pwfile.readline() + pwfile.close() + return password.rstrip() + + +def _RunCommand(cmd, dry_run, shell=False, echo_cmd=True): + """Runs the command if dry_run is false, otherwise just prints the command.""" + if echo_cmd: + print cmd + if not dry_run: + return subprocess.call(cmd, shell=shell) + else: + return 0 + + +def _ReleaseLock(lock_file, lock_filename): + """Removes the lockfile. Function-ized so we can bail from anywhere""" + os.close(lock_file) + os.remove(lock_filename) + + +def run_coverity(options, args): + """Runs all the selected tests for the given build type and target.""" + # Create the lock file to prevent another instance of this script from + # running. + lock_filename = os.path.join(options.source_dir, LOCK_FILE) + try: + lock_file = os.open(lock_filename, + os.O_CREAT | os.O_EXCL | os.O_TRUNC | os.O_RDWR) + except OSError, err: + print 'Failed to open lock file:\n ' + str(err) + return 1 + + # Write the pid of this script (the python.exe process) to the lock file. + os.write(lock_file, str(os.getpid())) + + options.target = options.target.title() + + start_time = time.time() + + print 'Change directory to ' + options.source_dir + os.chdir(options.source_dir) + + # The coverity-password filename may have been a relative path. + # If so, assume it's relative to the source directory, which means + # the time to read the password is after we do the chdir(). + coverity_password = _ReadPassword(options.coverity_password_file) + + cmd = 'gclient sync' + gclient_exit = _RunCommand(cmd, options.dry_run, shell=True) + if gclient_exit != 0: + print 'gclient aborted with status %s' % gclient_exit + _ReleaseLock(lock_file, lock_filename) + return 1 + + print 'Elapsed time: %ds' % (time.time() - start_time) + + # Do a clean build. Remove the build output directory first. + if sys.platform.startswith('linux'): + rm_path = os.path.join(options.source_dir,'out',options.target) + elif sys.platform == 'win32': + rm_path = os.path.join(options.source_dir,options.solution_dir, + options.target) + elif sys.platform == 'darwin': + rm_path = os.path.join(options.source_dir,'xcodebuild') + else: + print 'Platform "%s" unrecognized, aborting' % sys.platform + _ReleaseLock(lock_file, lock_filename) + return 1 + + if options.dry_run: + print 'shutil.rmtree(%s)' % repr(rm_path) + else: + shutil.rmtree(rm_path,True) + + if options.preserve_intermediate_dir: + print 'Preserving intermediate directory.' + else: + if options.dry_run: + print 'shutil.rmtree(%s)' % repr(options.coverity_intermediate_dir) + print 'os.mkdir(%s)' % repr(options.coverity_intermediate_dir) + else: + shutil.rmtree(options.coverity_intermediate_dir,True) + os.mkdir(options.coverity_intermediate_dir) + + print 'Elapsed time: %ds' % (time.time() - start_time) + + use_shell_during_make = False + if sys.platform.startswith('linux'): + use_shell_during_make = True + _RunCommand('pwd', options.dry_run, shell=True) + cmd = '%s/cov-build --dir %s make BUILDTYPE=%s All' % ( + options.coverity_bin_dir, options.coverity_intermediate_dir, + options.target) + elif sys.platform == 'win32': + cmd = ('%s\\cov-build.exe --dir %s devenv.com %s\\%s /build %s ' + '/project webrtc.vcproj') % ( + options.coverity_bin_dir, options.coverity_intermediate_dir, + options.source_dir, options.solution_file, options.target) + elif sys.platform == 'darwin': + use_shell_during_make = True + _RunCommand('pwd', options.dry_run, shell=True) + cmd = ('%s/cov-build --dir %s xcodebuild -project webrtc.xcodeproj ' + '-configuration %s -target All') % ( + options.coverity_bin_dir, options.coverity_intermediate_dir, + options.target) + + + _RunCommand(cmd, options.dry_run, shell=use_shell_during_make) + print 'Elapsed time: %ds' % (time.time() - start_time) + + cov_analyze_exe = os.path.join(options.coverity_bin_dir,'cov-analyze') + cmd = '%s --dir %s %s' % (cov_analyze_exe, + options.coverity_intermediate_dir, + options.coverity_analyze_options) + _RunCommand(cmd, options.dry_run, shell=use_shell_during_make) + print 'Elapsed time: %ds' % (time.time() - start_time) + + cov_commit_exe = os.path.join(options.coverity_bin_dir,'cov-commit-defects') + + # On Linux we have started using a Target with a space in it, so we want + # to quote it. On the other hand, Windows quoting doesn't work quite the + # same way. To be conservative, I'd like to avoid quoting an argument + # that doesn't need quoting and which we haven't historically been quoting + # on that platform. So, only quote the target if we have to. + coverity_target = options.coverity_target + if sys.platform != 'win32': + coverity_target = '"%s"' % coverity_target + + cmd = ('%s --dir %s --host %s --port %s ' + '--stream %s ' + '--target %s ' + '--user %s ' + '--password %s') % (cov_commit_exe, + options.coverity_intermediate_dir, + options.coverity_dbhost, + options.coverity_port, + options.coverity_stream, + coverity_target, + options.coverity_user, + coverity_password) + # Avoid echoing the Commit command because it has a password in it + print 'Commiting defects to Coverity Integrity Manager server...' + _RunCommand(cmd, options.dry_run, shell=use_shell_during_make, echo_cmd=False) + + print 'Completed! Total time: %ds' % (time.time() - start_time) + + _ReleaseLock(lock_file, lock_filename) + + return 0 + + +def main(): + option_parser = optparse.OptionParser() + option_parser.add_option('', '--dry-run', action='store_true', default=False, + help='print but don\'t run the commands') + + option_parser.add_option('', '--target', default='Release', + help='build target (Debug or Release)') + + option_parser.add_option('', '--source-dir', dest='source_dir', + help='full path to directory ABOVE "src"', + default=WEBRTC_SOURCE_DIR) + + option_parser.add_option('', '--solution-file', dest='solution_file', + help='filename of solution file to build (Win only)', + default=WEBRTC_SOLUTION_FILE) + + option_parser.add_option('', '--solution-dir', dest='solution_dir', + help='build directory for the solution (Win only)', + default=WEBRTC_SOLUTION_DIR) + + option_parser.add_option('', '--coverity-bin-dir', dest='coverity_bin_dir', + default=COVERITY_BIN_DIR) + + option_parser.add_option('', '--coverity-intermediate-dir', + dest='coverity_intermediate_dir', + default=COVERITY_INTERMEDIATE_DIR) + + option_parser.add_option('', '--coverity-analyze-options', + dest='coverity_analyze_options', + help=('all cov-analyze options, e.g. "%s"' + % COVERITY_ANALYZE_OPTIONS), + default=COVERITY_ANALYZE_OPTIONS) + + option_parser.add_option('', '--coverity-db-host', + dest='coverity_dbhost', + help=('coverity defect db server hostname, e.g. %s' + % COVERITY_REMOTE), + default=COVERITY_REMOTE) + + option_parser.add_option('', '--coverity-db-port', dest='coverity_port', + help=('port # of coverity web/db server, e.g. %s' + % COVERITY_PORT), + default=COVERITY_PORT) + + option_parser.add_option('', '--coverity-stream', dest='coverity_stream', + help=('Name of stream reported to Coverity, e.g. %s' + % COVERITY_STREAM), + default=COVERITY_STREAM) + + option_parser.add_option('', '--coverity-target', dest='coverity_target', + help='Platform Target reported to coverity', + default=COVERITY_TARGET) + + option_parser.add_option('', '--coverity-user', dest='coverity_user', + help='Username used to log into coverity', + default=COVERITY_USER) + + option_parser.add_option('', '--coverity-password-file', + dest='coverity_password_file', + help='file containing the coverity password', + default='coverity-password') + + helpmsg = ('By default, the intermediate dir is emptied before analysis. ' + 'This switch disables that behavior.') + option_parser.add_option('', '--preserve-intermediate-dir', + action='store_true', help=helpmsg, + default=False) + + options, args = option_parser.parse_args() + return run_coverity(options, args) + + +if '__main__' == __name__: + sys.exit(main())