From 19b977bcdf62a9f6fca2c115fd85d2508f4a818b Mon Sep 17 00:00:00 2001 From: Yury Gribov Date: Thu, 18 Feb 2016 11:08:46 +0000 Subject: [PATCH] [analyzer] Add --force-analyze-debug-code option to scan-build to force debug build and hopefully enable more precise warnings. Static Analyzer is much more efficient when built in debug mode (-UNDEBUG) so we advice users to enable it manually. This may be inconvenient in case of large complex projects (think about Linux distros e.g. Android or Tizen). This patch adds a flag to scan-build which inserts -UNDEBUG automatically. Differential Revision: http://reviews.llvm.org/D16200 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261204 91177308-0d34-0410-b5e6-96231b3b80d8 --- tools/scan-build-py/libscanbuild/analyze.py | 16 +++++++++++++-- tools/scan-build-py/libscanbuild/runner.py | 11 ++++++++-- tools/scan-build-py/tests/unit/test_runner.py | 11 ++++++++++ tools/scan-build/bin/scan-build | 20 ++++++++++++++++--- tools/scan-build/libexec/ccc-analyzer | 8 ++++++++ www/analyzer/scan-build.html | 3 +++ 6 files changed, 62 insertions(+), 7 deletions(-) diff --git a/tools/scan-build-py/libscanbuild/analyze.py b/tools/scan-build-py/libscanbuild/analyze.py index 0d3547befe..9b00d04fc0 100644 --- a/tools/scan-build-py/libscanbuild/analyze.py +++ b/tools/scan-build-py/libscanbuild/analyze.py @@ -106,7 +106,8 @@ def run_analyzer(args, output_dir): 'output_dir': output_dir, 'output_format': args.output_format, 'output_failures': args.output_failures, - 'direct_args': analyzer_params(args) + 'direct_args': analyzer_params(args), + 'force_analyze_debug_code' : args.force_analyze_debug_code } logging.debug('run analyzer against compilation database') @@ -138,7 +139,9 @@ def setup_environment(args, destination, bin_dir): 'ANALYZE_BUILD_REPORT_DIR': destination, 'ANALYZE_BUILD_REPORT_FORMAT': args.output_format, 'ANALYZE_BUILD_REPORT_FAILURES': 'yes' if args.output_failures else '', - 'ANALYZE_BUILD_PARAMETERS': ' '.join(analyzer_params(args)) + 'ANALYZE_BUILD_PARAMETERS': ' '.join(analyzer_params(args)), + 'ANALYZE_BUILD_FORCE_ANALYZE_DEBUG_CODE' + : 'yes' if args.force_analyze_debug_code else '' }) return environment @@ -168,6 +171,8 @@ def analyze_build_wrapper(cplusplus): 'output_failures': os.getenv('ANALYZE_BUILD_REPORT_FAILURES'), 'direct_args': os.getenv('ANALYZE_BUILD_PARAMETERS', '').split(' '), + 'force_analyze_debug_code': + os.getenv('ANALYZE_BUILD_FORCE_ANALYZE_DEBUG_CODE'), 'directory': os.getcwd(), } # get relevant parameters from command line arguments @@ -450,6 +455,13 @@ def create_parser(from_build_command): Could be usefull when project contains 3rd party libraries. The directory path shall be absolute path as file names in the compilation database.""") + advanced.add_argument( + '--force-analyze-debug-code', + dest='force_analyze_debug_code', + action='store_true', + help="""Tells analyzer to enable assertions in code even if they were + disabled during compilation, enabling more precise + results.""") plugins = parser.add_argument_group('checker options') plugins.add_argument( diff --git a/tools/scan-build-py/libscanbuild/runner.py b/tools/scan-build-py/libscanbuild/runner.py index 248ca90ad3..63b9f74369 100644 --- a/tools/scan-build-py/libscanbuild/runner.py +++ b/tools/scan-build-py/libscanbuild/runner.py @@ -41,6 +41,7 @@ def require(required): @require(['command', 'directory', 'file', # an entry from compilation database 'clang', 'direct_args', # compiler name, and arguments from command + 'force_analyze_debug_code', # preprocessing options 'output_dir', 'output_format', 'output_failures']) def run(opts): """ Entry point to run (or not) static analyzer against a single entry @@ -164,9 +165,13 @@ def set_analyzer_output(opts, continuation=run_analyzer): opts.update({'output': ['-o', opts['output_dir']]}) return continuation(opts) +def force_analyze_debug_code(cmd): + """ Enable assert()'s by undefining NDEBUG. """ + cmd.append('-UNDEBUG') -@require(['file', 'directory', 'clang', 'direct_args', 'language', - 'output_dir', 'output_format', 'output_failures']) +@require(['file', 'directory', 'clang', 'direct_args', + 'force_analyze_debug_code', 'language', 'output_dir', + 'output_format', 'output_failures']) def create_commands(opts, continuation=set_analyzer_output): """ Create command to run analyzer or failure report generation. @@ -178,6 +183,8 @@ def create_commands(opts, continuation=set_analyzer_output): if 'arch' in opts: common.extend(['-arch', opts.pop('arch')]) common.extend(opts.pop('compile_options', [])) + if opts['force_analyze_debug_code']: + force_analyze_debug_code(common) common.extend(['-x', opts['language']]) common.append(os.path.relpath(opts['file'], opts['directory'])) diff --git a/tools/scan-build-py/tests/unit/test_runner.py b/tools/scan-build-py/tests/unit/test_runner.py index ea10051d85..de15d23692 100644 --- a/tools/scan-build-py/tests/unit/test_runner.py +++ b/tools/scan-build-py/tests/unit/test_runner.py @@ -211,3 +211,14 @@ class RequireDecoratorTest(unittest.TestCase): def test_method_exception_not_caught(self): self.assertRaises(Exception, method_exception_from_inside, dict()) + +class ForceAnalyzeDebugTest(unittest.TestCase): + + def test_force_analyze_debug_code(self): + for a, b in [ + ([], ['-UNDEBUG']), + (['-O2'], ['-O2', '-UNDEBUG']), + (['-Dkey=val'], ['-Dkey=val', '-UNDEBUG']), + (['-D', 'NDEBUG'], ['-D', 'NDEBUG', '-UNDEBUG']) ]: + sut.force_analyze_debug_code(a) + self.assertEqual(a, b) diff --git a/tools/scan-build/bin/scan-build b/tools/scan-build/bin/scan-build index 6a14484970..3182a29767 100755 --- a/tools/scan-build/bin/scan-build +++ b/tools/scan-build/bin/scan-build @@ -69,7 +69,8 @@ my %Options = ( MaxLoop => 0, PluginsToLoad => [], AnalyzerDiscoveryMethod => undef, - OverrideCompiler => 0 # The flag corresponding to the --override-compiler command line option. + OverrideCompiler => 0, # The flag corresponding to the --override-compiler command line option. + ForceAnalyzeDebugCode => 0 ); lock_keys(%Options); @@ -951,7 +952,8 @@ sub SetEnv { 'CCC_CC', 'CCC_CXX', 'CCC_REPORT_FAILURES', - 'CLANG_ANALYZER_TARGET') { + 'CLANG_ANALYZER_TARGET', + 'CCC_ANALYZER_FORCE_ANALYZE_DEBUG_CODE') { my $x = $EnvVars->{$var}; if (defined $x) { $ENV{$var} = $x } } @@ -1118,6 +1120,11 @@ OPTIONS: Also analyze functions in #included files. By default, such functions are skipped unless they are called by functions within the main source file. + --force-analyze-debug-code + + Tells analyzer to enable assertions in code even if they were disabled + during compilation to enable more precise results. + -o Specifies the output directory for analyzer reports. Subdirectories will be @@ -1681,6 +1688,12 @@ sub ProcessArgs { next; } + if ($arg eq "--force-analyze-debug-code") { + shift @$Args; + $Options{ForceAnalyzeDebugCode} = 1; + next; + } + DieDiag("unrecognized option '$arg'\n") if ($arg =~ /^-/); $NumArgs--; @@ -1796,7 +1809,8 @@ my %EnvVars = ( 'CCC_ANALYZER_CONSTRAINTS_MODEL' => $Options{ConstraintsModel}, 'CCC_ANALYZER_INTERNAL_STATS' => $Options{InternalStats}, 'CCC_ANALYZER_OUTPUT_FORMAT' => $Options{OutputFormat}, - 'CLANG_ANALYZER_TARGET' => $Options{AnalyzerTarget} + 'CLANG_ANALYZER_TARGET' => $Options{AnalyzerTarget}, + 'CCC_ANALYZER_FORCE_ANALYZE_DEBUG_CODE' => $Options{ForceAnalyzeDebugCode} ); # Run the build. diff --git a/tools/scan-build/libexec/ccc-analyzer b/tools/scan-build/libexec/ccc-analyzer index 831dd42e9c..bfda1d326f 100755 --- a/tools/scan-build/libexec/ccc-analyzer +++ b/tools/scan-build/libexec/ccc-analyzer @@ -492,6 +492,9 @@ if (defined $ENV{'CCC_ANALYZER_LOG'}) { $Verbose = 2; } # Get the HTML output directory. my $HtmlDir = $ENV{'CCC_ANALYZER_HTML'}; +# Get force-analyze-debug-code option. +my $ForceAnalyzeDebugCode = $ENV{'CCC_ANALYZER_FORCE_ANALYZE_DEBUG_CODE'}; + my %DisabledArchs = ('ppc' => 1, 'ppc64' => 1); my %ArchsSeen; my $HadArch = 0; @@ -682,6 +685,11 @@ foreach (my $i = 0; $i < scalar(@ARGV); ++$i) { } } +# Forcedly enable debugging if requested by user. +if ($ForceAnalyzeDebugCode) { + push @CompileOpts, '-UNDEBUG'; +} + # If we are on OSX and have an installation where the # default SDK is inferred by xcrun use xcrun to infer # the SDK. diff --git a/www/analyzer/scan-build.html b/www/analyzer/scan-build.html index 04e93232a6..b16f6bb53f 100644 --- a/www/analyzer/scan-build.html +++ b/www/analyzer/scan-build.html @@ -226,6 +226,9 @@ Assertions are picked up by the static analyzer to prune infeasible paths, which in some cases can greatly reduce the number of false positives (bogus error reports) emitted by the tool.

+

Another option is to use --force-analyze-debug-code flag of +scan-build tool which would enable assertions automatically.

+

Use verbose output when debugging scan-build

scan-build takes a -v option to emit verbose output about -- 2.40.0